add host and tags params to main page

This commit is contained in:
NlightN22 2024-11-30 17:58:27 +07:00
parent e2bd0c20c5
commit ab3bf6f162
5 changed files with 93 additions and 62 deletions

View File

@ -3,13 +3,13 @@ import { notifications } from '@mantine/notifications';
import { IconAlertCircle } from '@tabler/icons-react'; import { IconAlertCircle } from '@tabler/icons-react';
import { t } from 'i18next'; import { t } from 'i18next';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useContext, useEffect, useMemo } from 'react'; import { useContext, useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import { Context } from '..'; import { Context } from '..';
import { isStartBiggerThanEndTime } from '../shared/utils/dateUtil'; import { isStartBiggerThanEndTime } from '../shared/utils/dateUtil';
import EventsBody from '../widgets/EventsBody'; import EventsBody from '../widgets/EventsBody';
import EventsRightFilters from '../widgets/sidebars/EventsRightFilters'; import EventsRightFilters from '../widgets/sidebars/EventsRightFilters';
import { SideBarContext } from '../widgets/sidebars/SideBarContext'; import { SideBarContext } from '../widgets/sidebars/SideBarContext';
import { useLocation, useSearchParams } from 'react-router-dom';
export const eventsQueryParams = { export const eventsQueryParams = {
hostId: 'hostId', hostId: 'hostId',
@ -28,6 +28,13 @@ const EventsPage = () => {
useEffect(() => { useEffect(() => {
setRightChildren(<EventsRightFilters />) setRightChildren(<EventsRightFilters />)
const paramHostId = searchParams.get(eventsQueryParams.hostId) || undefined
const paramCameraId = searchParams.get(eventsQueryParams.cameraId) || undefined
const paramStartDate = searchParams.get(eventsQueryParams.startDate) || undefined
const paramEndDate = searchParams.get(eventsQueryParams.endDate) || undefined
const paramStartTime = searchParams.get(eventsQueryParams.startTime) || undefined
const paramEndTime = searchParams.get(eventsQueryParams.endTime) || undefined
eventsStore.loadFiltersFromPage(paramHostId, paramCameraId, paramStartDate, paramEndDate, paramStartTime, paramEndTime)
return () => setRightChildren(null) return () => setRightChildren(null)
}, []) }, [])
@ -37,13 +44,7 @@ const EventsPage = () => {
const { hostId, cameraId, period, startTime, endTime } = eventsStore.filters const { hostId, cameraId, period, startTime, endTime } = eventsStore.filters
useEffect(() => { useEffect(() => {
const paramHostId = searchParams.get(eventsQueryParams.hostId) || undefined
const paramCameraId = searchParams.get(eventsQueryParams.cameraId) || undefined
const paramStartDate = searchParams.get(eventsQueryParams.startDate) || undefined
const paramEndDate = searchParams.get(eventsQueryParams.endDate) || undefined
const paramStartTime = searchParams.get(eventsQueryParams.startTime) || undefined
const paramEndTime = searchParams.get(eventsQueryParams.endTime) || undefined
eventsStore.loadFiltersFromPage(paramHostId, paramCameraId, paramStartDate, paramEndDate, paramStartTime, paramEndTime)
}, [searchParams]) }, [searchParams])
useEffect(() => { useEffect(() => {

View File

@ -1,4 +1,5 @@
import { Flex, Grid } from '@mantine/core'; import { Flex, Grid } from '@mantine/core';
import { IconSearch } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useContext, useEffect, useMemo, useState } from 'react'; import { useContext, useEffect, useMemo, useState } from 'react';
@ -7,24 +8,30 @@ import { Context } from '..';
import { useRealmUser } from '../hooks/useRealmUser'; import { useRealmUser } from '../hooks/useRealmUser';
import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api'; import { frigateApi, frigateQueryKeys } from '../services/frigate.proxy/frigate.api';
import { GetCameraWHostWConfig } from '../services/frigate.proxy/frigate.schema'; import { GetCameraWHostWConfig } from '../services/frigate.proxy/frigate.schema';
import ClearableTextInput from '../shared/components/inputs/ClearableTextInput';
import CenterLoader from '../shared/components/loaders/CenterLoader'; import CenterLoader from '../shared/components/loaders/CenterLoader';
import { isProduction } from '../shared/env.const'; import { isProduction } from '../shared/env.const';
import CameraCard from '../widgets/CameraCard'; import CameraCard from '../widgets/CameraCard';
import MainFiltersRightSide from '../widgets/sidebars/MainFiltersRightSide'; import MainFiltersRightSide from '../widgets/sidebars/MainFiltersRightSide';
import { SideBarContext } from '../widgets/sidebars/SideBarContext'; import { SideBarContext } from '../widgets/sidebars/SideBarContext';
import RetryErrorPage from './RetryErrorPage'; import RetryErrorPage from './RetryErrorPage';
import { IconSearch } from '@tabler/icons-react'; import { useSearchParams } from 'react-router-dom';
import ClearableTextInput from '../shared/components/inputs/ClearableTextInput';
import { CameraTag } from '../types/tags'; export const mainPageParams = {
hostId: 'hostId',
selectedTags: 'selectedTags',
searchQuery: 'searchQuery',
}
const MainPage = () => { const MainPage = () => {
const { t } = useTranslation() const { t } = useTranslation()
const { mainStore } = useContext(Context) const { mainStore } = useContext(Context)
const [searchParams] = useSearchParams()
const { setRightChildren } = useContext(SideBarContext) const { setRightChildren } = useContext(SideBarContext)
const { selectedHostId, selectedTags } = mainStore const { hostId: selectedHostId, selectedTags } = mainStore.filters
const [searchQuery, setSearchQuery] = useState<string>() const [searchQuery, setSearchQuery] = useState<string>()
const [filteredCameras, setFilteredCameras] = useState<GetCameraWHostWConfig[]>() const [filteredCameras, setFilteredCameras] = useState<GetCameraWHostWConfig[]>()
const [filteredTags, setFilteredTags] = useState<CameraTag[]>()
const realmUser = useRealmUser() const realmUser = useRealmUser()
if (!isProduction) console.log('Realmuser:', realmUser) if (!isProduction) console.log('Realmuser:', realmUser)
@ -36,6 +43,13 @@ const MainPage = () => {
useEffect(() => { useEffect(() => {
setRightChildren(<MainFiltersRightSide />); setRightChildren(<MainFiltersRightSide />);
const serializedTags = searchParams.get(mainPageParams.selectedTags)
const deSerializedTags = serializedTags ? mainStore.getArrayParam(serializedTags) : []
mainStore.loadFiltersFromPage({
hostId: searchParams.get(mainPageParams.hostId) || undefined,
searchQuery: searchParams.get(mainPageParams.searchQuery) || undefined,
selectedTags: deSerializedTags,
})
return () => setRightChildren(null); return () => setRightChildren(null);
}, []); }, []);
@ -48,7 +62,7 @@ const MainPage = () => {
const filterCameras = (camera: GetCameraWHostWConfig) => { const filterCameras = (camera: GetCameraWHostWConfig) => {
const matchesHostId = selectedHostId ? camera.frigateHost?.id === selectedHostId : true const matchesHostId = selectedHostId ? camera.frigateHost?.id === selectedHostId : true
const matchesSearchQuery = searchQuery ? camera.name.toLowerCase().includes(searchQuery.toLowerCase()) : true const matchesSearchQuery = searchQuery ? camera.name.toLowerCase().includes(searchQuery.toLowerCase()) : true
const matchesTags = selectedTags.length === 0 || camera.tags.some( tag => selectedTags.includes(tag.id)) const matchesTags = selectedTags.length === 0 || camera.tags.some( tag => selectedTags.includes(tag.id))
return matchesHostId && matchesSearchQuery && matchesTags return matchesHostId && matchesSearchQuery && matchesTags
} }

View File

@ -15,20 +15,6 @@ export class EventsStore {
constructor() { constructor() {
makeAutoObservable(this); makeAutoObservable(this);
// this.loadFiltersFromURL();
}
loadFiltersFromURL() {
const params = new URLSearchParams(window.location.search);
this.filters.hostId = params.get(eventsQueryParams.hostId) || undefined;
this.filters.cameraId = params.get(eventsQueryParams.cameraId) || undefined;
const startDate = params.get(eventsQueryParams.startDate);
const endDate = params.get(eventsQueryParams.endDate);
if (startDate && endDate) {
this.filters.period = [new Date(startDate), new Date(endDate)]
}
this.filters.startTime = params.get(eventsQueryParams.startTime) || undefined
this.filters.endTime = params.get(eventsQueryParams.endTime) || undefined
} }
loadFiltersFromPage( loadFiltersFromPage(
@ -74,20 +60,20 @@ export class EventsStore {
updateURL(navigate: (path: string, options?: NavigateOptions) => void) { updateURL(navigate: (path: string, options?: NavigateOptions) => void) {
const params = new URLSearchParams(); const params = new URLSearchParams();
if (this.filters.hostId) params.set('hostId', this.filters.hostId); if (this.filters.hostId) params.set(eventsQueryParams.hostId, this.filters.hostId);
if (this.filters.cameraId) params.set('cameraId', this.filters.cameraId); if (this.filters.cameraId) params.set(eventsQueryParams.cameraId, this.filters.cameraId);
if (this.filters.period) { if (this.filters.period) {
const [startDate, endDate] = this.filters.period; const [startDate, endDate] = this.filters.period;
if (startDate instanceof Date && !isNaN(startDate.getTime())) { if (startDate instanceof Date && !isNaN(startDate.getTime())) {
params.set('startDate', startDate.toISOString()); params.set(eventsQueryParams.startDate, startDate.toISOString());
} }
if (endDate instanceof Date && !isNaN(endDate.getTime())) { if (endDate instanceof Date && !isNaN(endDate.getTime())) {
params.set('endDate', endDate.toISOString()); params.set(eventsQueryParams.endDate, endDate.toISOString());
} }
} }
if (this.filters.startTime) params.set('startTime', this.filters.startTime) if (this.filters.startTime) params.set(eventsQueryParams.startTime, this.filters.startTime)
if (this.filters.endTime) params.set('endTime', this.filters.endTime) if (this.filters.endTime) params.set(eventsQueryParams.endTime, this.filters.endTime)
navigate(`?${params.toString()}`, { replace: true }); navigate(`?${params.toString()}`, { replace: true });
} }

View File

@ -1,26 +1,57 @@
import { makeAutoObservable } from "mobx"; import { makeAutoObservable } from "mobx";
import { CameraTag } from "../../types/tags"; import { NavigateOptions } from "react-router-dom";
import { mainPageParams } from "../../pages/MainPage";
import { isProduction } from "../env.const";
interface Filters {
hostId?: string | null
searchQuery?: string | null
selectedTags: string[]
}
export class MainStore { export class MainStore {
filters: Filters = { selectedTags: []}
private _selectedHostId: string | undefined
public get selectedHostId(): string | undefined {
return this._selectedHostId;
}
public set selectedHostId(value: string | undefined) {
this._selectedHostId = value;
}
private _selectedTags: string[] = [];
public get selectedTags(): string[] {
return this._selectedTags;
}
public set selectedTags(value: string[]) {
this._selectedTags = value;
}
constructor() { constructor() {
makeAutoObservable(this) makeAutoObservable(this)
} }
loadFiltersFromPage(filters: Filters) {
if (!isProduction) console.log('MainPage load filters')
this.filters = filters
}
updateURL(navigate: (path: string, options?: NavigateOptions) => void) {
const params = new URLSearchParams();
if (this.filters.hostId) params.set(mainPageParams.hostId, this.filters.hostId);
if (this.filters.searchQuery) params.set(mainPageParams.searchQuery, this.filters.searchQuery);
if (this.filters.selectedTags) {
const serializedTags = this.filters.selectedTags.join(",")
params.set(mainPageParams.selectedTags, serializedTags);
}
navigate(`?${params.toString()}`, { replace: true });
}
setHostId(hostId: string, navigate: (path: string, options?: NavigateOptions) => void) {
this.filters.hostId = hostId;
this.updateURL(navigate)
}
setSearchQuery(searchQuery: string, navigate: (path: string, options?: NavigateOptions) => void) {
this.filters.searchQuery = searchQuery;
this.updateURL(navigate)
}
setSelectedTags(selectedTags: string[], navigate: (path: string, options?: NavigateOptions) => void) {
if (!isProduction) console.log('MainPage set filters.selectedTags ', selectedTags)
this.filters.selectedTags = selectedTags ? selectedTags : []
this.updateURL(navigate)
}
getArrayParam = (key: string): string[] => {
const searchParams = new URLSearchParams(window.location.search);
const paramValue = searchParams.get(key);
return paramValue ? paramValue.split(",") : [];
}
} }

View File

@ -5,33 +5,32 @@ import { Context } from '../..';
import HostSelect from '../../shared/components/filters/HostSelect'; import HostSelect from '../../shared/components/filters/HostSelect';
import UserTagsFilter from '../../shared/components/filters/UserTagsFilter'; import UserTagsFilter from '../../shared/components/filters/UserTagsFilter';
import { isProduction } from '../../shared/env.const'; import { isProduction } from '../../shared/env.const';
import { useNavigate } from 'react-router-dom';
const MainFiltersRightSide = () => { const MainFiltersRightSide = () => {
const { t } = useTranslation() const { t } = useTranslation()
const navigate = useNavigate()
const { mainStore } = useContext(Context) const { mainStore } = useContext(Context)
const { selectedHostId } = mainStore const { hostId } = mainStore.filters
const handleSelectHost = (value: string) => { const handleSelectHost = (value: string) => {
if (!isProduction) console.log('handleSelectHost value', value) if (!isProduction) console.log('handleSelectHost value', value)
mainStore.selectedHostId = value mainStore.setHostId(value, navigate)
} }
const handleSelectTags = (tags: string[]) => { const handleSelectTags = (tags: string[]) => {
if (!isProduction) console.log('handleSelectTags value', tags) if (!isProduction) console.log('handleSelectTags value', tags)
mainStore.selectedTags = tags mainStore.setSelectedTags(tags, navigate)
} }
return ( return (
<> <>
<HostSelect <HostSelect
label={t('selectHost')} label={t('selectHost')}
valueId={selectedHostId} valueId={hostId || undefined}
defaultId={selectedHostId} defaultId={hostId || undefined}
onChange={handleSelectHost} onChange={handleSelectHost}
/> />