mirror of
https://github.com/pds-nest/nest.git
synced 2024-11-25 14:34:19 +00:00
💥 Refactor more more more more things
This commit is contained in:
parent
2bcfb3bdf7
commit
850adb5ba5
13 changed files with 52 additions and 154 deletions
|
@ -1,65 +1,22 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useContext } from "react"
|
||||||
import { faAt, faClock, faGlobe, faHashtag, faMapPin } from "@fortawesome/free-solid-svg-icons"
|
|
||||||
import ContextRepositoryEditor from "../../contexts/ContextRepositoryEditor"
|
import ContextRepositoryEditor from "../../contexts/ContextRepositoryEditor"
|
||||||
import Badge from "../base/Badge"
|
import Badge from "../base/Badge"
|
||||||
|
|
||||||
|
|
||||||
const CONDITION_COLORS = {
|
|
||||||
0: "Grey", // Hashtag
|
|
||||||
2: "Yellow", // Time
|
|
||||||
3: "Red", // Coordinates
|
|
||||||
4: "Red", // Place
|
|
||||||
5: "Green", // User
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const CONDITION_ICONS = {
|
|
||||||
0: faHashtag, // Hashtag
|
|
||||||
2: faClock, // Time
|
|
||||||
3: faGlobe, // Coordinates
|
|
||||||
4: faMapPin, // Place
|
|
||||||
5: faAt, // User
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Badge} representing a Condition for a filter.
|
* A {@link Badge} representing a {@link Condition}.
|
||||||
*
|
*
|
||||||
* @param condition - The Condition that this badge represents.
|
* @param condition - The {@link Condition} that this badge represents.
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export default function BadgeCondition({ ...condition }) {
|
export default function BadgeCondition({ condition }) {
|
||||||
const { id, type, content } = condition
|
|
||||||
const color = CONDITION_COLORS[type]
|
|
||||||
const icon = CONDITION_ICONS[type]
|
|
||||||
const { removeRawCondition } = useContext(ContextRepositoryEditor)
|
const { removeRawCondition } = useContext(ContextRepositoryEditor)
|
||||||
|
|
||||||
let displayedContent = content
|
|
||||||
if(type === 3) {
|
|
||||||
let split = displayedContent.split(" ")
|
|
||||||
let radius = Number.parseFloat(split[1]).toFixed(0)
|
|
||||||
let radiusType = "m"
|
|
||||||
if(radius >= 2000) {
|
|
||||||
radius = Math.round(radius / 1000)
|
|
||||||
radiusType = "km"
|
|
||||||
}
|
|
||||||
let lat = Number(split[2]).toFixed(3)
|
|
||||||
let lng = Number(split[3]).toFixed(3)
|
|
||||||
displayedContent = `${split[0]} ${radius}${radiusType} ${lat} ${lng}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Badge
|
<Badge
|
||||||
title={id ? `💠 Condition ID: ${id}` : "✨ New Condition"}
|
{...condition.display()}
|
||||||
color={color}
|
onClickDelete={() => removeRawCondition(condition)}
|
||||||
icon={icon}
|
/>
|
||||||
onClickDelete={() => {
|
|
||||||
console.debug(`Removing Condition: `, condition)
|
|
||||||
removeRawCondition(condition)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{displayedContent}
|
|
||||||
</Badge>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
.ConditionBadge {
|
|
||||||
display: inline-flex;
|
|
||||||
|
|
||||||
gap: 5px;
|
|
||||||
padding: 0 5px;
|
|
||||||
border-radius: 25px;
|
|
||||||
margin: 0 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ConditionBadgeRed {
|
|
||||||
background-color: var(--bg-red);
|
|
||||||
color: var(--fg-red)
|
|
||||||
}
|
|
||||||
|
|
||||||
.ConditionBadgeYellow {
|
|
||||||
background-color: var(--bg-yellow);
|
|
||||||
color: var(--fg-yellow)
|
|
||||||
}
|
|
||||||
|
|
||||||
.ConditionBadgeGrey {
|
|
||||||
background-color: var(--bg-grey);
|
|
||||||
color: var(--fg-grey)
|
|
||||||
}
|
|
||||||
|
|
||||||
.ConditionBadgeGreen {
|
|
||||||
background-color: var(--bg-green);
|
|
||||||
color: var(--fg-green)
|
|
||||||
}
|
|
||||||
|
|
||||||
.Text {
|
|
||||||
max-width: 350px;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Icon {
|
|
||||||
width: 15px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useContext } from "react"
|
||||||
import Badge from "../base/Badge"
|
|
||||||
import ContextRepositoryViewer from "../../contexts/ContextRepositoryViewer"
|
import ContextRepositoryViewer from "../../contexts/ContextRepositoryViewer"
|
||||||
|
import Badge from "../base/Badge"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,11 +15,8 @@ export default function BadgeFilter({ filter }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Badge
|
<Badge
|
||||||
color={filter.color()}
|
{...filter.display()}
|
||||||
icon={filter.icon()}
|
|
||||||
onClickDelete={() => removeFilter(filter)}
|
onClickDelete={() => removeFilter(filter)}
|
||||||
>
|
/>
|
||||||
{filter.text()}
|
|
||||||
</Badge>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useCallback, useContext } from "react"
|
||||||
import BoxFull from "../base/BoxFull"
|
import BoxFull from "../base/BoxFull"
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||||
import { faClock } from "@fortawesome/free-solid-svg-icons"
|
import { faClock } from "@fortawesome/free-solid-svg-icons"
|
||||||
import useRepositoryEditor from "../../hooks/useRepositoryEditor"
|
import useRepositoryEditor from "../../hooks/useRepositoryEditor"
|
||||||
import Condition from "../../utils/Condition"
|
|
||||||
import convertToLocalISODate from "../../utils/convertToLocalISODate"
|
|
||||||
import ContextLanguage from "../../contexts/ContextLanguage"
|
import ContextLanguage from "../../contexts/ContextLanguage"
|
||||||
import FormInlineBADatetime from "./FormInlineBADatetime"
|
import FormInlineBADatetime from "./FormInlineBADatetime"
|
||||||
|
import { ConditionTime } from "../../objects/Condition"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link BoxFull} that allows the user to select a Twitter user to search for, and then to add it as a Condition
|
* A {@link BoxFull} that allows the user to select a Twitter user to search for, and then to add it as a
|
||||||
* to the {@link ContextRepositoryEditor}.
|
* {@link ConditionTime} of a RepositoryEditor.
|
||||||
*
|
*
|
||||||
* @param props - Additional props to pass to the box.
|
* @param props - Additional props to pass to the box.
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
|
@ -21,14 +20,10 @@ export default function BoxConditionDatetime({ ...props }) {
|
||||||
const { addCondition } = useRepositoryEditor()
|
const { addCondition } = useRepositoryEditor()
|
||||||
const { strings } = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
|
|
||||||
const submit = ({ date, isBefore }) => {
|
const submit = useCallback(
|
||||||
if(date.toString() === "Invalid Date") {
|
timeRay => addCondition(new ConditionTime(timeRay)),
|
||||||
console.debug("Refusing to add condition: ", date, " is an Invalid Date.")
|
[addCondition]
|
||||||
return
|
)
|
||||||
}
|
|
||||||
const aware = convertToLocalISODate(date)
|
|
||||||
addCondition(new Condition("TIME", `${isBefore ? "<" : ">"} ${aware}`))
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BoxFull
|
<BoxFull
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
.Input {
|
|
||||||
flex-shrink: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Button {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,16 +1,16 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useCallback, useContext } from "react"
|
||||||
import BoxFull from "../base/BoxFull"
|
import BoxFull from "../base/BoxFull"
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||||
import { faHashtag } from "@fortawesome/free-solid-svg-icons"
|
import { faHashtag } from "@fortawesome/free-solid-svg-icons"
|
||||||
import useRepositoryEditor from "../../hooks/useRepositoryEditor"
|
import useRepositoryEditor from "../../hooks/useRepositoryEditor"
|
||||||
import Condition from "../../utils/Condition"
|
|
||||||
import ContextLanguage from "../../contexts/ContextLanguage"
|
import ContextLanguage from "../../contexts/ContextLanguage"
|
||||||
import FormInlineHashtag from "./FormInlineHashtag"
|
import FormInlineHashtag from "./FormInlineHashtag"
|
||||||
|
import { ConditionHashtag } from "../../objects/Condition"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link BoxFull} that allows the user to select a Twitter hashtag to search for, and then to add it as a Condition
|
* A {@link BoxFull} that allows the user to select a Twitter hashtag to search for, and then to add it as a
|
||||||
* to the {@link ContextRepositoryEditor}.
|
* {@link ConditionHashtag} of a RepositoryEditor.
|
||||||
*
|
*
|
||||||
* @param props - Additional props to pass to the box.
|
* @param props - Additional props to pass to the box.
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
|
@ -20,9 +20,10 @@ export default function BoxConditionHashtag({ ...props }) {
|
||||||
const { addCondition } = useRepositoryEditor()
|
const { addCondition } = useRepositoryEditor()
|
||||||
const { strings } = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
|
|
||||||
const submit = value => {
|
const submit = useCallback(
|
||||||
addCondition(new Condition("HASHTAG", value))
|
value => addCondition(new ConditionHashtag(value)),
|
||||||
}
|
[addCondition]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BoxFull
|
<BoxFull
|
||||||
|
|
|
@ -1,36 +1,29 @@
|
||||||
import React, { useCallback, useContext } from "react"
|
import React, { useCallback, useContext } from "react"
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||||
import { faMapPin, faPlus } from "@fortawesome/free-solid-svg-icons"
|
import { faLocationArrow, faMapPin, faPlus } from "@fortawesome/free-solid-svg-icons"
|
||||||
import ButtonIconOnly from "../base/ButtonIconOnly"
|
import ButtonIconOnly from "../base/ButtonIconOnly"
|
||||||
import useRepositoryEditor from "../../hooks/useRepositoryEditor"
|
import useRepositoryEditor from "../../hooks/useRepositoryEditor"
|
||||||
import Condition from "../../utils/Condition"
|
|
||||||
import ContextLanguage from "../../contexts/ContextLanguage"
|
import ContextLanguage from "../../contexts/ContextLanguage"
|
||||||
import BoxMap from "../base/BoxMap"
|
import BoxMap from "../base/BoxMap"
|
||||||
import useMapAreaState from "../../hooks/useMapAreaState"
|
import useMapAreaState from "../../hooks/useMapAreaState"
|
||||||
import osmZoomLevels from "../../utils/osmZoomLevels"
|
import { ConditionLocation } from "../../objects/Condition"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link BoxFull} that allows the user to select a geographical location to use to filter tweets.
|
* A {@link BoxMap} that allows the user to select a geographical location, and then to add it as a
|
||||||
|
* {@link ConditionLocation} of a RepositoryEditor.
|
||||||
*
|
*
|
||||||
* @param props - Additional props to pass to the box.
|
* @param props - Additional props to pass to the box.
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export default function BoxConditionMap({ ...props }) {
|
export default function BoxConditionLocation({ ...props }) {
|
||||||
const mapViewHook = useMapAreaState()
|
const mapViewHook = useMapAreaState()
|
||||||
const { addCondition } = useRepositoryEditor()
|
const { addCondition } = useRepositoryEditor()
|
||||||
const { strings } = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
|
|
||||||
const onButtonClick = useCallback(
|
const onButtonClick = useCallback(
|
||||||
() => {
|
() => addCondition(new ConditionLocation(mapViewHook.mapArea)),
|
||||||
const radius = mapViewHook.zoom * osmZoomLevels[mapViewHook.zoom]
|
|
||||||
|
|
||||||
addCondition(new Condition(
|
|
||||||
"COORDINATES",
|
|
||||||
`< ${radius} ${mapViewHook.center.lat} ${mapViewHook.center.lng}`,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
[mapViewHook, addCondition]
|
[mapViewHook, addCondition]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -41,7 +34,7 @@ export default function BoxConditionMap({ ...props }) {
|
||||||
<span>
|
<span>
|
||||||
{strings.searchBy}
|
{strings.searchBy}
|
||||||
|
|
||||||
<FontAwesomeIcon icon={faMapPin}/>
|
<FontAwesomeIcon icon={faLocationArrow}/>
|
||||||
|
|
||||||
{strings.byZone}
|
{strings.byZone}
|
||||||
</span>
|
</span>
|
|
@ -1,16 +1,16 @@
|
||||||
import React, { useContext } from "react"
|
import React, { useCallback, useContext } from "react"
|
||||||
import BoxFull from "../base/BoxFull"
|
import BoxFull from "../base/BoxFull"
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||||
import { faAt } from "@fortawesome/free-solid-svg-icons"
|
import { faAt } from "@fortawesome/free-solid-svg-icons"
|
||||||
import useRepositoryEditor from "../../hooks/useRepositoryEditor"
|
import useRepositoryEditor from "../../hooks/useRepositoryEditor"
|
||||||
import Condition from "../../utils/Condition"
|
|
||||||
import ContextLanguage from "../../contexts/ContextLanguage"
|
import ContextLanguage from "../../contexts/ContextLanguage"
|
||||||
import FormInlineUser from "./FormInlineUser"
|
import FormInlineUser from "./FormInlineUser"
|
||||||
|
import { ConditionHashtag, ConditionUser } from "../../objects/Condition"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link BoxFull} that allows the user to select a Twitter user to search for, and then to add it as a Condition
|
* A {@link BoxFull} that allows the user to select a Twitter user to search for, and then to add it as a
|
||||||
* to the {@link ContextRepositoryEditor}.
|
* {@link ConditionUser} of a RepositoryEditor.
|
||||||
*
|
*
|
||||||
* @param props - Additional props to pass to the box.
|
* @param props - Additional props to pass to the box.
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
|
@ -20,9 +20,10 @@ export default function BoxConditionUser({ ...props }) {
|
||||||
const { addCondition } = useRepositoryEditor()
|
const { addCondition } = useRepositoryEditor()
|
||||||
const { strings } = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
|
|
||||||
const submit = value => {
|
const submit = useCallback(
|
||||||
addCondition(new Condition("USER", value))
|
value => addCondition(new ConditionUser(value)),
|
||||||
}
|
[addCondition]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BoxFull
|
<BoxFull
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default function BoxConditions({ ...props }) {
|
||||||
const { conditions } = useRepositoryEditor()
|
const { conditions } = useRepositoryEditor()
|
||||||
const { strings } = useContext(ContextLanguage)
|
const { strings } = useContext(ContextLanguage)
|
||||||
|
|
||||||
const badges = conditions.map((cond, pos) => <BadgeCondition key={pos} {...cond}/>)
|
const badges = conditions.map((cond, pos) => <BadgeCondition key={pos} condition={cond}/>)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BoxFull header={strings.conditions} {...props}>
|
<BoxFull header={strings.conditions} {...props}>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { faClock, faPlus } from "@fortawesome/free-solid-svg-icons"
|
||||||
import ButtonIconOnly from "../base/ButtonIconOnly"
|
import ButtonIconOnly from "../base/ButtonIconOnly"
|
||||||
import Style from "./FormInlineText.module.css"
|
import Style from "./FormInlineText.module.css"
|
||||||
import ButtonToggleBeforeAfter from "./ButtonToggleBeforeAfter"
|
import ButtonToggleBeforeAfter from "./ButtonToggleBeforeAfter"
|
||||||
|
import TimeRay from "../../objects/TimeRay"
|
||||||
|
|
||||||
|
|
||||||
const INVALID_CHARACTERS = /[^0-9TZ:+-]/g
|
const INVALID_CHARACTERS = /[^0-9TZ:+-]/g
|
||||||
|
@ -26,10 +27,7 @@ export default function FormInlineBADatetime(
|
||||||
|
|
||||||
const _onSubmit = event => {
|
const _onSubmit = event => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
submit({
|
submit(new TimeRay(isBefore, new Date(value)))
|
||||||
date: new Date(value),
|
|
||||||
isBefore,
|
|
||||||
})
|
|
||||||
setValue("")
|
setValue("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import isString from "is-string"
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export default function GlobalServer({ children }) {
|
export default function GlobalServer({ children }) {
|
||||||
|
// TODO: Set this using an envvar
|
||||||
const [server, setServer] = useLocalStorageState("server", "http://127.0.0.1:5000")
|
const [server, setServer] = useLocalStorageState("server", "http://127.0.0.1:5000")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -50,13 +50,13 @@ export default function GlobalUser({ children }) {
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
const login = useCallback(async (email, password) => {
|
const login = useCallback(async (email, password) => {
|
||||||
console.debug("Contattando il server per accedere...")
|
console.debug("Contacting the server to login...")
|
||||||
const data = await fetchData("POST", `/api/v1/login`, {
|
const data = await fetchData("POST", `/api/v1/login`, {
|
||||||
"email": email,
|
"email": email,
|
||||||
"password": password,
|
"password": password,
|
||||||
})
|
})
|
||||||
|
|
||||||
console.debug("Memorizzando lo stato di login...")
|
console.debug("Saving login state...")
|
||||||
setUser({
|
setUser({
|
||||||
email: data["user"]["email"],
|
email: data["user"]["email"],
|
||||||
isAdmin: data["user"]["isAdmin"],
|
isAdmin: data["user"]["isAdmin"],
|
||||||
|
@ -64,18 +64,18 @@ export default function GlobalUser({ children }) {
|
||||||
token: data["access_token"],
|
token: data["access_token"],
|
||||||
})
|
})
|
||||||
|
|
||||||
console.info("Accesso effettuato!")
|
console.info("Login successful!")
|
||||||
}, [fetchData, setUser])
|
}, [fetchData, setUser])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logout from the currently active server.
|
* Logout from the currently active server.
|
||||||
*/
|
*/
|
||||||
const logout = useCallback(() => {
|
const logout = useCallback(() => {
|
||||||
console.debug("Ripulendo lo stato di login...")
|
console.debug("Clearing login state...")
|
||||||
setUser(null)
|
setUser(null)
|
||||||
console.debug("Stato di login ripulito!")
|
console.debug("Cleared login state!")
|
||||||
|
|
||||||
console.info("Logout avvenuto con successo!")
|
console.info("Logout successful!")
|
||||||
}, [setUser])
|
}, [setUser])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { useCallback, useContext, useMemo, useState } from "react"
|
||||||
import ContextRepositoryEditor from "../../contexts/ContextRepositoryEditor"
|
import ContextRepositoryEditor from "../../contexts/ContextRepositoryEditor"
|
||||||
import useArrayState from "../../hooks/useArrayState"
|
import useArrayState from "../../hooks/useArrayState"
|
||||||
import Style from "./RepositoryEditor.module.css"
|
import Style from "./RepositoryEditor.module.css"
|
||||||
import BoxConditionMap from "../interactive/BoxConditionMap"
|
import BoxConditionLocation from "../interactive/BoxConditionLocation"
|
||||||
import BoxConditionHashtag from "../interactive/BoxConditionHashtag"
|
import BoxConditionHashtag from "../interactive/BoxConditionHashtag"
|
||||||
import BoxConditionUser from "../interactive/BoxConditionUser"
|
import BoxConditionUser from "../interactive/BoxConditionUser"
|
||||||
import BoxConditionDatetime from "../interactive/BoxConditionDatetime"
|
import BoxConditionDatetime from "../interactive/BoxConditionDatetime"
|
||||||
|
@ -142,7 +142,7 @@ export default function RepositoryEditor({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={classNames(Style.RepositoryEditor, className)}>
|
<div className={classNames(Style.RepositoryEditor, className)}>
|
||||||
<BoxConditionMap className={Style.SearchByZone}/>
|
<BoxConditionLocation className={Style.SearchByZone}/>
|
||||||
<BoxConditionHashtag className={Style.SearchByHashtags}/>
|
<BoxConditionHashtag className={Style.SearchByHashtags}/>
|
||||||
<BoxConditionUser className={Style.SearchByUser}/>
|
<BoxConditionUser className={Style.SearchByUser}/>
|
||||||
<BoxConditionDatetime className={Style.SearchByTimePeriod}/>
|
<BoxConditionDatetime className={Style.SearchByTimePeriod}/>
|
||||||
|
|
Loading…
Reference in a new issue