diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 35200d0..a5cc46e 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -16,6 +16,7 @@ "BlockedIPs": "Blocked IPs", "Cancel": "Cancel", "Changes": "Changes", + "Close": "Close", "Confirm": "Confirm", "ConfirmEmailResent": "Confirm e-mail resent", "ClientDomainManager": "Client Domain Manager", @@ -68,6 +69,7 @@ "PasswordIsRequired": "Password is required", "PasswordMinLength": "Password must be at least {{minPasswordLength}} characters", "PasswordsMustMatch": "You need to confirm by typing exactly the same as the new password", + "PressAgainToDelete": "Press again to delete", "PressAgainToUnblock": "Press again to unblock", "ResetPassword": "Reset Password", "ResendConfirm": "Resend Confirm", diff --git a/src/components/common/AutoComplete.tsx b/src/components/common/AutoComplete.tsx index 7ff0b3d..20262d4 100644 --- a/src/components/common/AutoComplete.tsx +++ b/src/components/common/AutoComplete.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React, { useState, useCallback, useEffect, useRef } from "react"; import Option from "./option"; interface AutocompleteProps { @@ -8,119 +8,109 @@ interface AutocompleteProps { onSelect: (item: Option) => void; } -interface AutocompleteState { - filteredOptions: Option[]; -} +const Autocomplete: React.FC = ({ + options, + selectedOptions, + placeholder, + onSelect, +}) => { + const [filteredOptions, setFilteredOptions] = useState([]); + const inputRef = useRef(null); -export default class Autocomplete extends React.PureComponent< - AutocompleteProps, - AutocompleteState -> { - private inputRef; - constructor(props: AutocompleteProps) { - super(props); - this.state = { filteredOptions: [] }; - this.inputRef = React.createRef(); - } + const filterOptions = useCallback( + (filterTerm: string) => { + if (filterTerm !== "") { + let filtered = + options?.filter((x) => + x.name.toLowerCase().includes(filterTerm.toLowerCase()), + ) ?? []; + filtered = filtered.filter( + (x) => !selectedOptions?.some((y) => x._id === y._id), + ); + setFilteredOptions(filtered); + } else { + setFilteredOptions([]); + } + }, + [options, selectedOptions], + ); - private filterOptions(filterTerm: string) { - if (filterTerm !== "") { - let filtered = - this.props.options?.filter((x) => - x.name.toLowerCase().includes(filterTerm.toLowerCase()), - ) ?? []; - filtered = filtered.filter( - (x) => !this.props.selectedOptions?.some((y) => x._id === y._id), - ); - this.setState({ - filteredOptions: filtered, - }); - } else { - this.setState({ - filteredOptions: [], - }); - } - } - - private showOptions() { - let filtered = this.props.options?.filter((x) => x.name) ?? []; + const showOptions = useCallback(() => { + let filtered = options?.filter((x) => x.name) ?? []; filtered = filtered.filter( - (x) => !this.props.selectedOptions?.some((y) => x._id === y._id), + (x) => !selectedOptions?.some((y) => x._id === y._id), ); - this.setState({ - filteredOptions: filtered, - }); - } + setFilteredOptions(filtered); + }, [options, selectedOptions]); - private hideAutocomplete = (event: any) => { + const hideAutocomplete = useCallback((event: any) => { if (event.target.classList.contains("autocomplete-text-input")) return; + setFilteredOptions([]); + }, []); - this.setState({ - filteredOptions: [], - }); - }; - - private handleBlur = (event: React.FocusEvent) => { + const handleBlur = useCallback((event: React.FocusEvent) => { setTimeout(() => { if ( - this.inputRef.current && - this.inputRef.current.contains(document.activeElement) + inputRef.current && + inputRef.current.contains(document.activeElement) ) { return; } - - this.setState({ filteredOptions: [] }); + setFilteredOptions([]); }, 0); - }; + }, []); - componentDidMount() { - document.addEventListener("click", this.hideAutocomplete); - } + useEffect(() => { + document.addEventListener("click", hideAutocomplete); + return () => { + document.removeEventListener("click", hideAutocomplete); + }; + }, [hideAutocomplete]); - componentWillUnmount() { - document.removeEventListener("click", this.hideAutocomplete); - } + const handleSelect = useCallback( + (item: Option) => { + onSelect(item); + setFilteredOptions([]); + }, + [onSelect], + ); - render() { - const { placeholder, onSelect } = this.props; - const { filteredOptions } = this.state; + return ( +
+ { + filterOptions(e.target.value); + }} + onFocus={() => { + showOptions(); + }} + placeholder={placeholder} + /> + {filteredOptions.length > 0 && ( +
    + {filteredOptions.map((x) => ( +
  • + +
  • + ))} +
+ )} +
+ ); +}; - return ( -
- { - this.filterOptions(e.target.value); - }} - onFocus={(e) => { - this.showOptions(); - }} - placeholder={placeholder} - /> - {filteredOptions.length > 0 && ( -
    - {filteredOptions.map((x, i) => ( -
  • - -
  • - ))} -
- )} -
- ); - } -} +export default Autocomplete; diff --git a/src/components/common/HorizionalTabs.tsx b/src/components/common/HorizionalTabs.tsx index aa483ee..a1434ac 100644 --- a/src/components/common/HorizionalTabs.tsx +++ b/src/components/common/HorizionalTabs.tsx @@ -1,5 +1,4 @@ import React, { useEffect, useState, useCallback, useMemo } from "react"; -import { Navigate } from "react-router-dom"; import TabHeader from "./TabHeader"; interface HorizontalTabsProps { @@ -8,7 +7,6 @@ interface HorizontalTabsProps { const HorizontalTabs: React.FC = ({ children }) => { const [activeTab, setActiveTab] = useState(""); - const [redirect, setRedirect] = useState(""); // Set initial tab on mount useEffect(() => { @@ -26,10 +24,6 @@ const HorizontalTabs: React.FC = ({ children }) => { return match ? match.props.children : <>; }, [children, activeTab]); - if (redirect !== "") { - return ; - } - // If only one tab, just render its content if (children.length === 1) { return <>{activeTabChildren}; diff --git a/src/components/common/Pill.tsx b/src/components/common/Pill.tsx index bcb9db3..f8a59ce 100644 --- a/src/components/common/Pill.tsx +++ b/src/components/common/Pill.tsx @@ -1,52 +1,54 @@ import { faXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as React from "react"; +import { useTranslation } from "react-i18next"; +import { Namespaces } from "../../i18n/i18n"; interface PillProps { - pillKey: any; - displayText: string; - enabled?: boolean; - onClick: (item: any) => void; - className?: string; - readOnly?: boolean; - flash?: boolean; + pillKey: any; + displayText: string; + enabled?: boolean; + onClick: (item: any) => void; + className?: string; + readOnly?: boolean; + flash?: boolean; } export default function Pill({ - pillKey, - displayText, - enabled = true, - className, - onClick, - readOnly, - flash = false + pillKey, + displayText, + enabled = true, + className, + onClick, + readOnly, + flash = false, }: PillProps) { - const handleOnClick = () => { - onClick(pillKey); - }; + const { t } = useTranslation(Namespaces.Common); - let classNames = "pill"; + const handleOnClick = () => { + onClick(pillKey); + }; - if (className) - classNames += " " + className; + let classNames = "pill"; - if (flash) - classNames += " flash"; + if (className) classNames += " " + className; - return ( -
- {displayText} - {!readOnly && ( - - )} -
- ); + if (flash) classNames += " flash"; + + return ( +
+ {displayText} + {!readOnly && ( + + )} +
+ ); } diff --git a/src/components/common/TableBody.tsx b/src/components/common/TableBody.tsx index db6af43..c54da24 100644 --- a/src/components/common/TableBody.tsx +++ b/src/components/common/TableBody.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { useTranslation } from "react-i18next"; import deepFind from "../../utils/deepfind"; import Column from "./columns"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -46,6 +47,7 @@ export default function TableBody({ onSelectRow, showSecondaryAudit, }: TableBodyProps): JSX.Element { + const { t } = useTranslation(); const resolvePath = (path: string, args: string[]) => { let modifiedPath = path; let index = 0; @@ -137,7 +139,7 @@ export default function TableBody({ buttonType={ButtonType.primary} keyValue={item} onClick={onDelete} - confirmMessage={"Press again to delete"} + confirmMessage={t("PressAgainToDelete")} > diff --git a/src/components/common/option.tsx b/src/components/common/option.tsx index 1a64817..46702bc 100644 --- a/src/components/common/option.tsx +++ b/src/components/common/option.tsx @@ -1,6 +1,4 @@ -import { GeneralIdRef } from "../../utils/GeneralIdRef"; - export default interface Option { - _id: string | number; - name: string; + _id: string | number; + name: string; } diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index aeb80c2..2bf3363 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -6,7 +6,6 @@ import { determineInitialLocale } from "../modules/frame/services/lanugageServic export const Namespaces = { Common: "common", - Frame: "frame", } as const; export type Namespace = (typeof Namespaces)[keyof typeof Namespaces];