Next round of refactoring done
This commit is contained in:
parent
81a01fe9f3
commit
4a2d65f360
@ -16,6 +16,7 @@
|
|||||||
"BlockedIPs": "Blocked IPs",
|
"BlockedIPs": "Blocked IPs",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
"Changes": "Changes",
|
"Changes": "Changes",
|
||||||
|
"Close": "Close",
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
"ConfirmEmailResent": "Confirm e-mail resent",
|
"ConfirmEmailResent": "Confirm e-mail resent",
|
||||||
"ClientDomainManager": "Client Domain Manager",
|
"ClientDomainManager": "Client Domain Manager",
|
||||||
@ -68,6 +69,7 @@
|
|||||||
"PasswordIsRequired": "Password is required",
|
"PasswordIsRequired": "Password is required",
|
||||||
"PasswordMinLength": "Password must be at least {{minPasswordLength}} characters",
|
"PasswordMinLength": "Password must be at least {{minPasswordLength}} characters",
|
||||||
"PasswordsMustMatch": "You need to confirm by typing exactly the same as the new password",
|
"PasswordsMustMatch": "You need to confirm by typing exactly the same as the new password",
|
||||||
|
"PressAgainToDelete": "Press again to delete",
|
||||||
"PressAgainToUnblock": "Press again to unblock",
|
"PressAgainToUnblock": "Press again to unblock",
|
||||||
"ResetPassword": "Reset Password",
|
"ResetPassword": "Reset Password",
|
||||||
"ResendConfirm": "Resend Confirm",
|
"ResendConfirm": "Resend Confirm",
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import * as React from "react";
|
import React, { useState, useCallback, useEffect, useRef } from "react";
|
||||||
import Option from "./option";
|
import Option from "./option";
|
||||||
|
|
||||||
interface AutocompleteProps {
|
interface AutocompleteProps {
|
||||||
@ -8,119 +8,109 @@ interface AutocompleteProps {
|
|||||||
onSelect: (item: Option) => void;
|
onSelect: (item: Option) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AutocompleteState {
|
const Autocomplete: React.FC<AutocompleteProps> = ({
|
||||||
filteredOptions: Option[];
|
options,
|
||||||
}
|
selectedOptions,
|
||||||
|
placeholder,
|
||||||
|
onSelect,
|
||||||
|
}) => {
|
||||||
|
const [filteredOptions, setFilteredOptions] = useState<Option[]>([]);
|
||||||
|
const inputRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
export default class Autocomplete extends React.PureComponent<
|
const filterOptions = useCallback(
|
||||||
AutocompleteProps,
|
(filterTerm: string) => {
|
||||||
AutocompleteState
|
if (filterTerm !== "") {
|
||||||
> {
|
let filtered =
|
||||||
private inputRef;
|
options?.filter((x) =>
|
||||||
constructor(props: AutocompleteProps) {
|
x.name.toLowerCase().includes(filterTerm.toLowerCase()),
|
||||||
super(props);
|
) ?? [];
|
||||||
this.state = { filteredOptions: [] };
|
filtered = filtered.filter(
|
||||||
this.inputRef = React.createRef<HTMLDivElement>();
|
(x) => !selectedOptions?.some((y) => x._id === y._id),
|
||||||
}
|
);
|
||||||
|
setFilteredOptions(filtered);
|
||||||
|
} else {
|
||||||
|
setFilteredOptions([]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[options, selectedOptions],
|
||||||
|
);
|
||||||
|
|
||||||
private filterOptions(filterTerm: string) {
|
const showOptions = useCallback(() => {
|
||||||
if (filterTerm !== "") {
|
let filtered = options?.filter((x) => x.name) ?? [];
|
||||||
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) ?? [];
|
|
||||||
filtered = filtered.filter(
|
filtered = filtered.filter(
|
||||||
(x) => !this.props.selectedOptions?.some((y) => x._id === y._id),
|
(x) => !selectedOptions?.some((y) => x._id === y._id),
|
||||||
);
|
);
|
||||||
this.setState({
|
setFilteredOptions(filtered);
|
||||||
filteredOptions: filtered,
|
}, [options, selectedOptions]);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private hideAutocomplete = (event: any) => {
|
const hideAutocomplete = useCallback((event: any) => {
|
||||||
if (event.target.classList.contains("autocomplete-text-input")) return;
|
if (event.target.classList.contains("autocomplete-text-input")) return;
|
||||||
|
setFilteredOptions([]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
this.setState({
|
const handleBlur = useCallback((event: React.FocusEvent) => {
|
||||||
filteredOptions: [],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleBlur = (event: React.FocusEvent) => {
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (
|
if (
|
||||||
this.inputRef.current &&
|
inputRef.current &&
|
||||||
this.inputRef.current.contains(document.activeElement)
|
inputRef.current.contains(document.activeElement)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setFilteredOptions([]);
|
||||||
this.setState({ filteredOptions: [] });
|
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
componentDidMount() {
|
useEffect(() => {
|
||||||
document.addEventListener("click", this.hideAutocomplete);
|
document.addEventListener("click", hideAutocomplete);
|
||||||
}
|
return () => {
|
||||||
|
document.removeEventListener("click", hideAutocomplete);
|
||||||
|
};
|
||||||
|
}, [hideAutocomplete]);
|
||||||
|
|
||||||
componentWillUnmount() {
|
const handleSelect = useCallback(
|
||||||
document.removeEventListener("click", this.hideAutocomplete);
|
(item: Option) => {
|
||||||
}
|
onSelect(item);
|
||||||
|
setFilteredOptions([]);
|
||||||
|
},
|
||||||
|
[onSelect],
|
||||||
|
);
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const { placeholder, onSelect } = this.props;
|
<div
|
||||||
const { filteredOptions } = this.state;
|
className="autocomplete"
|
||||||
|
ref={inputRef}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
tabIndex={-1}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
className="autocomplete-text-input"
|
||||||
|
type="text"
|
||||||
|
onChange={(e) => {
|
||||||
|
filterOptions(e.target.value);
|
||||||
|
}}
|
||||||
|
onFocus={() => {
|
||||||
|
showOptions();
|
||||||
|
}}
|
||||||
|
placeholder={placeholder}
|
||||||
|
/>
|
||||||
|
{filteredOptions.length > 0 && (
|
||||||
|
<ul className="autocomplete-options">
|
||||||
|
{filteredOptions.map((x) => (
|
||||||
|
<li key={x._id} value={x._id} tabIndex={0}>
|
||||||
|
<button
|
||||||
|
className="autocomplete-option text-left"
|
||||||
|
onClick={() => {
|
||||||
|
handleSelect(x);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{x.name}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
export default Autocomplete;
|
||||||
<div
|
|
||||||
className="autocomplete"
|
|
||||||
ref={this.inputRef}
|
|
||||||
onBlur={this.handleBlur}
|
|
||||||
tabIndex={-1}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
className="autocomplete-text-input"
|
|
||||||
type="text"
|
|
||||||
onChange={(e) => {
|
|
||||||
this.filterOptions(e.target.value);
|
|
||||||
}}
|
|
||||||
onFocus={(e) => {
|
|
||||||
this.showOptions();
|
|
||||||
}}
|
|
||||||
placeholder={placeholder}
|
|
||||||
/>
|
|
||||||
{filteredOptions.length > 0 && (
|
|
||||||
<ul className="autocomplete-options">
|
|
||||||
{filteredOptions.map((x, i) => (
|
|
||||||
<li key={x._id} value={x._id} tabIndex={0}>
|
|
||||||
<button
|
|
||||||
className="autocomplete-option text-left"
|
|
||||||
onClick={(e) => {
|
|
||||||
onSelect(x);
|
|
||||||
this.setState({ filteredOptions: [] });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{x.name}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useState, useCallback, useMemo } from "react";
|
import React, { useEffect, useState, useCallback, useMemo } from "react";
|
||||||
import { Navigate } from "react-router-dom";
|
|
||||||
import TabHeader from "./TabHeader";
|
import TabHeader from "./TabHeader";
|
||||||
|
|
||||||
interface HorizontalTabsProps {
|
interface HorizontalTabsProps {
|
||||||
@ -8,7 +7,6 @@ interface HorizontalTabsProps {
|
|||||||
|
|
||||||
const HorizontalTabs: React.FC<HorizontalTabsProps> = ({ children }) => {
|
const HorizontalTabs: React.FC<HorizontalTabsProps> = ({ children }) => {
|
||||||
const [activeTab, setActiveTab] = useState<string>("");
|
const [activeTab, setActiveTab] = useState<string>("");
|
||||||
const [redirect, setRedirect] = useState<string>("");
|
|
||||||
|
|
||||||
// Set initial tab on mount
|
// Set initial tab on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -26,10 +24,6 @@ const HorizontalTabs: React.FC<HorizontalTabsProps> = ({ children }) => {
|
|||||||
return match ? match.props.children : <></>;
|
return match ? match.props.children : <></>;
|
||||||
}, [children, activeTab]);
|
}, [children, activeTab]);
|
||||||
|
|
||||||
if (redirect !== "") {
|
|
||||||
return <Navigate to={redirect} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If only one tab, just render its content
|
// If only one tab, just render its content
|
||||||
if (children.length === 1) {
|
if (children.length === 1) {
|
||||||
return <>{activeTabChildren}</>;
|
return <>{activeTabChildren}</>;
|
||||||
|
|||||||
@ -1,52 +1,54 @@
|
|||||||
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Namespaces } from "../../i18n/i18n";
|
||||||
|
|
||||||
interface PillProps {
|
interface PillProps {
|
||||||
pillKey: any;
|
pillKey: any;
|
||||||
displayText: string;
|
displayText: string;
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
onClick: (item: any) => void;
|
onClick: (item: any) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
flash?: boolean;
|
flash?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Pill({
|
export default function Pill({
|
||||||
pillKey,
|
pillKey,
|
||||||
displayText,
|
displayText,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
className,
|
className,
|
||||||
onClick,
|
onClick,
|
||||||
readOnly,
|
readOnly,
|
||||||
flash = false
|
flash = false,
|
||||||
}: PillProps) {
|
}: PillProps) {
|
||||||
const handleOnClick = () => {
|
const { t } = useTranslation(Namespaces.Common);
|
||||||
onClick(pillKey);
|
|
||||||
};
|
|
||||||
|
|
||||||
let classNames = "pill";
|
const handleOnClick = () => {
|
||||||
|
onClick(pillKey);
|
||||||
|
};
|
||||||
|
|
||||||
if (className)
|
let classNames = "pill";
|
||||||
classNames += " " + className;
|
|
||||||
|
|
||||||
if (flash)
|
if (className) classNames += " " + className;
|
||||||
classNames += " flash";
|
|
||||||
|
|
||||||
return (
|
if (flash) classNames += " flash";
|
||||||
<div className={classNames}>
|
|
||||||
{displayText}
|
return (
|
||||||
{!readOnly && (
|
<div className={classNames}>
|
||||||
<button
|
{displayText}
|
||||||
type="button"
|
{!readOnly && (
|
||||||
className={`close ${!enabled ? 'd-none': ''}`}
|
<button
|
||||||
data-dismiss="alert"
|
type="button"
|
||||||
aria-label="Close"
|
className={`close ${!enabled ? "d-none" : ""}`}
|
||||||
onClick={handleOnClick}
|
data-dismiss="alert"
|
||||||
>
|
aria-label={t("Close") as string}
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
onClick={handleOnClick}
|
||||||
</button>
|
>
|
||||||
)}
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
</div>
|
</button>
|
||||||
);
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import deepFind from "../../utils/deepfind";
|
import deepFind from "../../utils/deepfind";
|
||||||
import Column from "./columns";
|
import Column from "./columns";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
@ -46,6 +47,7 @@ export default function TableBody<T>({
|
|||||||
onSelectRow,
|
onSelectRow,
|
||||||
showSecondaryAudit,
|
showSecondaryAudit,
|
||||||
}: TableBodyProps<T>): JSX.Element {
|
}: TableBodyProps<T>): JSX.Element {
|
||||||
|
const { t } = useTranslation();
|
||||||
const resolvePath = (path: string, args: string[]) => {
|
const resolvePath = (path: string, args: string[]) => {
|
||||||
let modifiedPath = path;
|
let modifiedPath = path;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
@ -137,7 +139,7 @@ export default function TableBody<T>({
|
|||||||
buttonType={ButtonType.primary}
|
buttonType={ButtonType.primary}
|
||||||
keyValue={item}
|
keyValue={item}
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
confirmMessage={"Press again to delete"}
|
confirmMessage={t("PressAgainToDelete")}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faTrash} />
|
<FontAwesomeIcon icon={faTrash} />
|
||||||
</ConfirmButton>
|
</ConfirmButton>
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
import { GeneralIdRef } from "../../utils/GeneralIdRef";
|
|
||||||
|
|
||||||
export default interface Option {
|
export default interface Option {
|
||||||
_id: string | number;
|
_id: string | number;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { determineInitialLocale } from "../modules/frame/services/lanugageServic
|
|||||||
|
|
||||||
export const Namespaces = {
|
export const Namespaces = {
|
||||||
Common: "common",
|
Common: "common",
|
||||||
Frame: "frame",
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type Namespace = (typeof Namespaces)[keyof typeof Namespaces];
|
export type Namespace = (typeof Namespaces)[keyof typeof Namespaces];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user