Skip to content

Commit

Permalink
Add 'Add OTP token' option
Browse files Browse the repository at this point in the history
The 'Add OTP token' option
creates a new OTP token
associated to a specific user.

The QR code has been generated
using the qrcode.react[1]
library.

[1]- https://github.com/zpao/qrcode.react
Signed-off-by: Carla Martinez <[email protected]>
  • Loading branch information
carma12 committed Jan 8, 2024
1 parent c5634c8 commit 75cbf93
Show file tree
Hide file tree
Showing 7 changed files with 951 additions and 1 deletion.
107 changes: 107 additions & 0 deletions src/components/Form/DateTimeSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React from "react";
// PatternFly
import {
DatePicker,
InputGroup,
TimePicker,
isValidDate,
yyyyMMddFormat,
} from "@patternfly/react-core";
// Utils
import {
parseFullDateStringToUTCFormat,
toGeneralizedTime,
} from "src/utils/utils";

interface PropsToDateTimeSelector {
timeValue: string; // Generalized time format ('YYYYMMDDHHMMSSZ')
setTimeValue: (timeValue: string) => void;
name: string;
ariaLabel?: string;
isDisabled?: boolean;
}

const DateTimeSelector = (props: PropsToDateTimeSelector) => {
const [valueDate, setValueDate] = React.useState<Date | null>(null);

// Parse the current date into 'Date' format
React.useEffect(() => {
if (props.timeValue) {
const date = parseFullDateStringToUTCFormat(props.timeValue);
setValueDate(date);
}
}, [props.timeValue]);

// On change date handler
const onDateChange = (
_event: React.FormEvent<HTMLInputElement>,
inputDate: string,
newFromDate: Date | undefined
) => {
if (newFromDate !== undefined) {
if (
valueDate &&
isValidDate(valueDate) &&
isValidDate(newFromDate) &&
inputDate === yyyyMMddFormat(newFromDate)
) {
newFromDate.setHours(valueDate.getHours());
newFromDate.setMinutes(valueDate.getMinutes());
}
if (
isValidDate(newFromDate) &&
inputDate === yyyyMMddFormat(newFromDate)
) {
setValueDate(newFromDate);
// Parse to generalized format
props.setTimeValue(toGeneralizedTime(newFromDate));
}
}
};

// On change time handler
const onTimeChange = (_event, time, hour, minute) => {
let updatedFromDate = new Date();
if (valueDate && isValidDate(valueDate)) {
updatedFromDate = valueDate;
}
updatedFromDate.setHours(hour);
updatedFromDate.setMinutes(minute);

setValueDate(updatedFromDate);
// Parse to generalized format
props.setTimeValue(toGeneralizedTime(updatedFromDate));
};

// Parse the current date into 'HH:MM' format
const hhMMFormat = (date: Date) => {
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");

return hours + ":" + minutes;
};

return (
<InputGroup>
<DatePicker
name={props.name}
value={valueDate !== null ? yyyyMMddFormat(valueDate) : ""}
onChange={onDateChange}
aria-label={props.ariaLabel || props.name}
placeholder="YYYY-MM-DD"
isDisabled={props.isDisabled || false}
/>
<TimePicker
name={props.name}
time={valueDate !== null ? hhMMFormat(valueDate) : ""}
aria-label={props.ariaLabel || props.name}
onChange={onTimeChange}
placeholder="HH:MM"
is24Hour={true}
isDisabled={props.isDisabled || false}
/>
</InputGroup>
);
};

export default DateTimeSelector;
20 changes: 19 additions & 1 deletion src/components/UserSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import DisableEnableUsers from "./modals/DisableEnableUsers";
import DeleteUsers from "./modals/DeleteUsers";
import RebuildAutoMembership from "./modals/RebuildAutoMembership";
import UnlockUser from "./modals/UnlockUser";
import AddOtpToken from "./modals/AddOtpToken";

export interface PropsToUserSettings {
originalUser: Partial<User>;
Expand Down Expand Up @@ -160,6 +161,12 @@ const UserSettings = (props: PropsToUserSettings) => {
return isLocked;
};

// 'Add OTP token' option
const [isAddOtpTokenModalOpen, setIsAddOtpTokenModalOpen] = useState(false);
const onCloseAddOtpTokenModal = () => {
setIsAddOtpTokenModalOpen(false);
};

// Kebab
const [isKebabOpen, setIsKebabOpen] = useState(false);

Expand Down Expand Up @@ -189,7 +196,12 @@ const UserSettings = (props: PropsToUserSettings) => {
>
Unlock
</DropdownItem>,
<DropdownItem key="add otp token">Add OTP token</DropdownItem>,
<DropdownItem
key="add otp token"
onClick={() => setIsAddOtpTokenModalOpen(true)}
>
Add OTP token
</DropdownItem>,
<DropdownItem
key="rebuild auto membership"
onClick={() => setIsRebuildAutoMembershipModalOpen(true)}
Expand Down Expand Up @@ -488,6 +500,12 @@ const UserSettings = (props: PropsToUserSettings) => {
entriesToRebuild={userToRebuild}
entity="users"
/>
<AddOtpToken
uid={props.user.uid}
isOpen={isAddOtpTokenModalOpen}
setIsOpen={(value: boolean) => setIsAddOtpTokenModalOpen(value)}
onClose={onCloseAddOtpTokenModal}
/>
</>
);
};
Expand Down
59 changes: 59 additions & 0 deletions src/components/layouts/HelperTextWithIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from "react";
// PatternFly
import { HelperText, HelperTextItem } from "@patternfly/react-core";
// Icons
import InfoIcon from "@patternfly/react-icons/dist/esm/icons/info-icon";
import QuestionIcon from "@patternfly/react-icons/dist/esm/icons/question-icon";
import ExclamationIcon from "@patternfly/react-icons/dist/esm/icons/exclamation-icon";
import CheckIcon from "@patternfly/react-icons/dist/esm/icons/check-icon";
import TimesIcon from "@patternfly/react-icons/dist/esm/icons/times-icon";

interface PropsToHelperTextWithIcon {
message: string | React.ReactNode;
useDefaultIcons?: boolean | true;
type: "info" | "question" | "warning" | "success" | "error";
}

/*
* Helper text component documentation:
* http://v4-archive.patternfly.org/v4/components/helper-text
*/

const HelperTextWithIcon = (props: PropsToHelperTextWithIcon) => {
const [icon, setIcon] = React.useState(<InfoIcon />);

React.useEffect(() => {
switch (props.type) {
case "info":
setIcon(<InfoIcon />);
break;
case "question":
setIcon(<QuestionIcon />);
break;
case "warning":
setIcon(<ExclamationIcon />);
break;
case "success":
setIcon(<CheckIcon />);
break;
case "error":
setIcon(<TimesIcon />);
break;
default:
setIcon(<InfoIcon />);
break;
}
}, [props.type]);

return (
<HelperText>
{props.useDefaultIcons ? (
<HelperTextItem>{props.message}</HelperTextItem>
) : (
<HelperTextItem icon={icon}>{props.message}</HelperTextItem>
)}
</HelperText>
);
};

export default HelperTextWithIcon;
Loading

0 comments on commit 75cbf93

Please sign in to comment.