react-icons/io#IoMdCloseCircle TypeScript Examples
The following examples show how to use
react-icons/io#IoMdCloseCircle.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: FilterBadge.tsx From hub with Apache License 2.0 | 6 votes |
FilterBadge = (props: Props) => {
return (
<div className={`badge bg-light rounded-pill me-2 mb-2 p-0 ps-2 border ${styles.badgeFilter}`}>
<div className="d-flex flex-row align-items-center">
<div className="position-relative">
{props.type && <small className="fw-normal me-1 text-uppercase">{props.type}:</small>}
{props.name}
</div>
<button
className={`btn btn-link btn-sm py-0 text-center ${styles.btn}`}
onClick={props.onClick}
aria-label={`Remove filter: ${props.type ? `${props.type} -` : ''} ${props.name}`}
>
<IoMdCloseCircle className={`position-relative ${styles.btnIcon}`} />
</button>
</div>
</div>
);
}
Example #2
Source File: UploadedFileInfo.tsx From nextclade with MIT License | 6 votes |
function FileStatusIcon({ hasErrors }: { hasErrors: boolean }) {
const ICON_SIZE = 70
if (hasErrors) {
return <IoMdCloseCircle size={ICON_SIZE} color={theme.danger} />
}
return <IoMdCheckmarkCircle size={ICON_SIZE} color={theme.success} />
}
Example #3
Source File: UploadedFileInfoCompact.tsx From nextclade with MIT License | 6 votes |
function FileStatusIcon({ hasErrors }: { hasErrors: boolean }) {
const ICON_SIZE = 30
if (hasErrors) {
return <IoMdCloseCircle size={ICON_SIZE} color={theme.danger} />
}
return <IoMdCheckmarkCircle size={ICON_SIZE} color={theme.success} />
}
Example #4
Source File: Card.tsx From hub with Apache License 2.0 | 4 votes |
MemberCard = (props: Props) => {
const { ctx, dispatch } = useContext(AppCtx);
const [isDeletingMember, setIsDeletingMember] = useState(false);
const dropdownMenu = useRef(null);
const [dropdownMenuStatus, setDropdownMenuStatus] = useState<boolean>(false);
const [modalStatus, setModalStatus] = useState<boolean>(false);
const closeDropdown = () => {
setDropdownMenuStatus(false);
};
useOutsideClick([dropdownMenu], dropdownMenuStatus, closeDropdown);
async function deleteMember() {
try {
setIsDeletingMember(true);
await API.deleteOrganizationMember(ctx.prefs.controlPanel.selectedOrg!, props.member.alias);
setIsDeletingMember(false);
if (props.member.alias === ctx.user!.alias) {
dispatch(unselectOrg());
} else {
props.onSuccess();
}
} catch (err: any) {
setIsDeletingMember(false);
if (err.kind !== ErrorKind.Unauthorized) {
let errorMessage = 'An error occurred removing member from the organization, please try again later.';
if (err.kind === ErrorKind.Forbidden) {
errorMessage = 'You do not have permissions to remove members from the organization.';
}
alertDispatcher.postAlert({
type: 'danger',
message: errorMessage,
});
} else {
props.onAuthError();
}
}
}
const isUser = props.member.alias === ctx.user!.alias;
const getFullName = (): string => {
let fullName = '';
if (props.member.firstName) {
fullName += `${props.member.firstName} `;
}
if (props.member.lastName) {
fullName += props.member.lastName;
}
return fullName;
};
return (
<div className="col-12 col-xxl-6 py-sm-3 py-2 px-0 px-xxl-3" data-testid="memberCard">
<div className="card h-100">
<div className="card-body d-flex flex-column h-100">
<div className="d-flex flex-row w-100 justify-content-between align-items-start">
<div
className={`d-flex align-items-center justify-content-center p-1 overflow-hidden me-2 border border-2 rounded-circle bg-white ${styles.imageWrapper} imageWrapper`}
>
<FaUser className={`fs-4 ${styles.image}`} />
</div>
<div className="flex-grow-1">
<div className="d-flex flex-row align-items-start">
<div className="h5 mb-1">
{props.member.firstName || props.member.lastName ? getFullName() : props.member.alias}
</div>
{!isUndefined(props.member.confirmed) && !props.member.confirmed && (
<div className={classnames('ms-3', { 'me-3': props.membersNumber > 1 })}>
<span className="badge bg-warning">Invitation not accepted yet</span>
</div>
)}
</div>
<div className="h6 text-muted me-1 fst-italic">{props.member.alias}</div>
</div>
{props.membersNumber > 1 && (
<>
{modalStatus && (
<Modal
className="d-inline-block"
closeButton={
<>
<button
className="btn btn-sm btn-outline-secondary"
onClick={() => setModalStatus(false)}
aria-label="Cancel"
>
<div className="d-flex flex-row align-items-center">
<IoMdCloseCircle className="me-2" />
<span>Cancel</span>
</div>
</button>
<button
className="btn btn-sm btn-danger ms-3"
onClick={(e) => {
e.preventDefault();
deleteMember();
}}
disabled={isDeletingMember}
aria-label={isUser ? 'Leave organization' : 'Remove member'}
>
<div className="d-flex flex-row align-items-center text-uppercase">
{isDeletingMember ? (
<>
<span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
<span className="ms-2">{isUser ? 'Leaving...' : 'Removing...'}</span>
</>
) : (
<>
{isUser ? (
<FaSignOutAlt className={`me-2 ${styles.btnIcon}`} />
) : (
<FaUserMinus className={`me-2 ${styles.btnIcon}`} />
)}
<span>{isUser ? 'Leave' : 'Remove'}</span>
</>
)}
</div>
</button>
</>
}
header={
<div className={`h3 flex-grow-1 m-2 ${styles.title}`}>
{isUser ? 'Leave ' : 'Remove from '} organization
</div>
}
onClose={() => setModalStatus(false)}
open
>
<div className="mt-3 mw-100 text-center">
<p>
{isUser
? 'Are you sure you want to leave this organization?'
: 'Are you sure you want to remove this member from this organization?'}
</p>
</div>
</Modal>
)}
<div className="ms-auto">
<div
ref={dropdownMenu}
className={classnames('dropdown-menu dropdown-menu-end p-0', styles.dropdownMenu, {
show: dropdownMenuStatus,
})}
>
<div className="dropdown-arrow" />
{isUser ? (
<button
className="dropdown-item btn btn-sm rounded-0 text-dark"
onClick={(e: ReactMouseEvent<HTMLButtonElement>) => {
e.preventDefault();
closeDropdown();
setModalStatus(true);
}}
aria-label="Open leave organization modal"
>
<div className="d-flex flex-row align-items-center">
<FaSignOutAlt className={`me-2 ${styles.btnIcon}`} />
<span>Leave</span>
</div>
</button>
) : (
<ActionBtn
className="dropdown-item btn btn-sm rounded-0 text-dark"
onClick={(e: ReactMouseEvent<HTMLButtonElement>) => {
e.preventDefault();
closeDropdown();
setModalStatus(true);
}}
action={AuthorizerAction.DeleteOrganizationMember}
label="Open leave organization modal"
>
<>
<FaUserMinus className={`me-2 ${styles.btnIcon}`} />
<span>Remove</span>
</>
</ActionBtn>
)}
</div>
<button
className={`btn btn-outline-secondary rounded-circle p-0 text-center ${styles.btnDropdown}`}
onClick={() => setDropdownMenuStatus(true)}
aria-label="Open menu"
aria-expanded={dropdownMenuStatus}
>
<BsThreeDotsVertical />
</button>
</div>
</>
)}
</div>
</div>
</div>
</div>
);
}
Example #5
Source File: Card.tsx From hub with Apache License 2.0 | 4 votes |
OrganizationCard = (props: Props) => {
const { ctx, dispatch } = useContext(AppCtx);
const [isLeaving, setIsLeaving] = useState(false);
const [isAccepting, setIsAccepting] = useState(false);
const dropdownMenu = useRef(null);
const [dropdownMenuStatus, setDropdownMenuStatus] = useState<boolean>(false);
const [leaveModalStatus, setLeaveModalStatus] = useState<boolean>(false);
const isMember =
!isUndefined(props.organization.confirmed) && !isNull(props.organization.confirmed) && props.organization.confirmed;
const closeDropdown = () => {
setDropdownMenuStatus(false);
};
useOutsideClick([dropdownMenu], dropdownMenuStatus, closeDropdown);
async function leaveOrganization() {
try {
setIsLeaving(true);
await API.deleteOrganizationMember(props.organization.name, ctx.user!.alias);
setIsLeaving(false);
closeDropdown();
props.onSuccess();
if (
!isUndefined(ctx.prefs.controlPanel.selectedOrg) &&
ctx.prefs.controlPanel.selectedOrg === props.organization.name
) {
dispatch(unselectOrg());
}
} catch (err: any) {
setIsLeaving(false);
if (err.kind !== ErrorKind.Unauthorized) {
closeDropdown();
alertDispatcher.postAlert({
type: 'danger',
message: 'An error occurred leaving the organization, please try again later.',
});
} else {
props.onAuthError();
}
}
}
async function confirmOrganizationMembership() {
setIsAccepting(true);
try {
await API.confirmOrganizationMembership(props.organization.name);
setIsAccepting(false);
props.onSuccess();
} catch {
setIsAccepting(false);
}
}
const hasDropdownContent =
!isUndefined(props.organization) &&
(!props.organization.confirmed ||
(props.organization.confirmed &&
isMember &&
props.organization.membersCount &&
props.organization.membersCount > 1));
return (
<div className="col-12 col-xxl-6 py-sm-3 py-2 px-0 px-xxl-3" data-testid="organizationCard">
<div className="card h-100">
<div className="card-body d-flex flex-column h-100">
<div className="d-flex flex-row w-100 justify-content-between align-items-start">
<div className="d-flex flex-row align-items-center w-100">
<div
className={`d-flex align-items-center justify-content-center overflow-hidden p-1 me-2 position-relative border border-3 bg-white rounded-circle ${styles.imageWrapper} imageWrapper`}
>
{!isUndefined(props.organization.logoImageId) ? (
<Image
alt={props.organization.displayName || props.organization.name}
imageId={props.organization.logoImageId}
className={`fs-4 ${styles.image}`}
placeholderIcon={<MdBusiness />}
/>
) : (
<MdBusiness className={styles.image} />
)}
</div>
<div className="text-truncate">
<div className={`h5 mb-0 text-truncate ${styles.title}`}>
{props.organization.displayName || props.organization.name}
</div>
</div>
{!isMember && (
<div className="ms-3">
<span className="badge bg-warning">Invitation not accepted yet</span>
</div>
)}
<div className="ms-auto">
<div
ref={dropdownMenu}
className={classnames('dropdown-menu dropdown-menu-end p-0', styles.dropdownMenu, {
show: dropdownMenuStatus,
})}
>
<div className={`dropdown-arrow ${styles.arrow}`} />
{props.organization.confirmed ? (
<>
{isMember && props.organization.membersCount && props.organization.membersCount > 1 && (
<button
className="dropdown-item btn btn-sm rounded-0 text-dark"
onClick={(e: ReactMouseEvent<HTMLButtonElement>) => {
e.preventDefault();
closeDropdown();
setLeaveModalStatus(true);
}}
aria-label="Open modal"
>
<div className="d-flex flex-row align-items-center">
<FaSignOutAlt className={`me-2 ${styles.btnIcon}`} />
<span>Leave</span>
</div>
</button>
)}
</>
) : (
<div>
<button
className="dropdown-item btn btn-sm rounded-0 text-dark"
onClick={(e: ReactMouseEvent<HTMLButtonElement>) => {
e.preventDefault();
confirmOrganizationMembership();
closeDropdown();
}}
disabled={isAccepting}
aria-label="Confirm membership"
>
<div className="d-flex flex-row align-items-center">
{isAccepting ? (
<>
<span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
<span className="ms-2">Accepting invitation...</span>
</>
) : (
<>
<FaEnvelopeOpenText className={`me-2 ${styles.btnIcon}`} />
<span>Accept invitation</span>
</>
)}
</div>
</button>
</div>
)}
</div>
{hasDropdownContent && (
<button
className={`ms-3 mb-2 btn btn-outline-secondary rounded-circle p-0 text-center ${styles.btnDropdown}`}
onClick={() => setDropdownMenuStatus(true)}
aria-label="Open menu"
aria-expanded={dropdownMenuStatus}
>
<BsThreeDotsVertical />
</button>
)}
</div>
</div>
{leaveModalStatus && (
<Modal
className={`d-inline-block ${styles.modal}`}
closeButton={
<>
<button
className="btn btn-sm btn-outline-secondary text-uppercase"
onClick={() => setLeaveModalStatus(false)}
aria-label="Close modal"
>
<div className="d-flex flex-row align-items-center">
<IoMdCloseCircle className="me-2" />
<span>Cancel</span>
</div>
</button>
<button
className="btn btn-sm btn-danger ms-3"
onClick={(e) => {
e.preventDefault();
leaveOrganization();
}}
disabled={isLeaving}
aria-label="Leave organization"
>
<div className="d-flex flex-row align-items-center text-uppercase">
{isLeaving ? (
<>
<span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
<span className="ms-2">Leaving...</span>
</>
) : (
<>
<FaSignOutAlt className={`me-2 ${styles.btnIcon}`} />
<span>Leave</span>
</>
)}
</div>
</button>
</>
}
header={<div className={`h3 m-2 flex-grow-1 ${styles.title}`}>Leave organization</div>}
onClose={() => setLeaveModalStatus(false)}
open
>
<div className="mt-3 mw-100 text-center">
<p>Are you sure you want to leave this organization?</p>
</div>
</Modal>
)}
</div>
{props.organization.homeUrl && (
<div className="mt-3 text-truncate">
<small className="text-muted text-uppercase me-1">Homepage: </small>
<ExternalLink
href={props.organization.homeUrl}
className={`text-reset ${styles.link}`}
label={`Open link ${props.organization.homeUrl}`}
>
{props.organization.homeUrl}
</ExternalLink>
</div>
)}
{props.organization.description && (
<div className="mt-2">
<p className="mb-0">{props.organization.description}</p>
</div>
)}
</div>
</div>
</div>
);
}
Example #6
Source File: DeletionModal.tsx From hub with Apache License 2.0 | 4 votes |
DeletionModal = (props: Props) => {
const [isDeleting, setIsDeleting] = useState(false);
const [isValidInput, setIsValidInput] = useState<boolean>(false);
const onInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setIsValidInput(e.target.value === props.repository.name);
};
async function deleteRepository() {
try {
setIsDeleting(true);
await API.deleteRepository(props.repository.name, props.organizationName);
setIsDeleting(false);
props.onSuccess();
} catch (err: any) {
setIsDeleting(false);
if (err.kind === ErrorKind.Unauthorized) {
props.onAuthError();
} else {
let errorMessage = 'An error occurred deleting the repository, please try again later.';
if (!isUndefined(props.organizationName) && err.kind === ErrorKind.Forbidden) {
errorMessage = 'You do not have permissions to delete the repository from the organization.';
}
alertDispatcher.postAlert({
type: 'danger',
message: errorMessage,
});
}
}
}
return (
<Modal
className="d-inline-block"
closeButton={
<>
<button
className="btn btn-sm btn-outline-secondary text-uppercase"
onClick={() => props.setDeletionModalStatus(false)}
aria-label="Cancel"
>
<div className="d-flex flex-row align-items-center">
<IoMdCloseCircle className="me-2" />
<span>Cancel</span>
</div>
</button>
<button
className="btn btn-sm btn-danger ms-3"
onClick={(e) => {
e.preventDefault();
deleteRepository();
}}
disabled={isDeleting || !isValidInput}
aria-label="Delete repository"
>
<div className="d-flex flex-row align-items-center text-uppercase">
{isDeleting ? (
<>
<span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
<span className="ms-2">Deleting...</span>
</>
) : (
<>
<FaTrashAlt className="me-2" />
<span>Delete</span>
</>
)}
</div>
</button>
</>
}
header={<div className={`h3 m-2 flex-grow-1 ${styles.title}`}>Delete repository</div>}
onClose={() => props.setDeletionModalStatus(false)}
open
>
<div className="mw-100">
<div className="alert alert-warning my-4">
<span className="fw-bold text-uppercase">Important:</span> Please read this carefully.
</div>
<p>If you delete this repository all packages belonging to it will be deleted.</p>
<p>
All information related to your repository or packages will be permanently deleted as well. This includes
packages' stars, users subscriptions to packages, webhooks, events and notifications.
</p>
<p>
<span className="fw-bold">This operation cannot be undone</span>.
</p>
<p>
Please type <span className="fw-bold">{props.repository.name}</span> to confirm:
</p>
<InputField type="text" name="repoName" autoComplete="off" value="" onChange={onInputChange} />
</div>
</Modal>
);
}
Example #7
Source File: DeleteOrg.tsx From hub with Apache License 2.0 | 4 votes |
DeleteOrganization = (props: Props) => {
const { dispatch } = useContext(AppCtx);
const [openStatus, setOpenStatus] = useState<boolean>(false);
const [isDeleting, setIsDeleting] = useState(false);
const [isValidInput, setIsValidInput] = useState<boolean>(false);
const onInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setIsValidInput(e.target.value === props.organization.name);
};
async function deleteOrganization() {
try {
setIsDeleting(true);
await API.deleteOrganization(props.organization.name);
dispatch(unselectOrg());
window.scrollTo(0, 0); // Scroll to top when org is deleted
setIsDeleting(false);
} catch (err: any) {
setIsDeleting(false);
if (err.kind === ErrorKind.Unauthorized) {
props.onAuthError();
} else {
let errorMessage = 'An error occurred deleting the organization, please try again later.';
if (err.kind === ErrorKind.Forbidden) {
errorMessage = 'You do not have permissions to delete the organization.';
}
alertDispatcher.postAlert({
type: 'danger',
message: errorMessage,
});
}
}
}
return (
<>
<div className="mt-4 mt-md-5">
<p className="mb-4">
Deleting your organization will also delete all the content that belongs to it. Please be certain as{' '}
<span className="fw-bold">this operation cannot be undone</span>.
</p>
<ActionBtn
className="btn btn-sm btn-danger"
onClick={(e: ReactMouseEvent<HTMLButtonElement>) => {
e.preventDefault();
setOpenStatus(true);
}}
action={AuthorizerAction.DeleteOrganization}
label="Open delete organization modal"
>
<div className="d-flex flex-row align-items-center text-uppercase">
<FaTrashAlt className="me-2" />
<div>Delete organization</div>
</div>
</ActionBtn>
</div>
<Modal
className="d-inline-block"
closeButton={
<>
<button
className="btn btn-sm btn-outline-secondary text-uppercase"
onClick={() => setOpenStatus(false)}
aria-label="Close"
>
<div className="d-flex flex-row align-items-center">
<IoMdCloseCircle className="me-2" />
<span>Cancel</span>
</div>
</button>
<button
className="btn btn-sm btn-danger ms-3"
onClick={(e) => {
e.preventDefault();
deleteOrganization();
}}
disabled={isDeleting || !isValidInput}
aria-label="Delete organization"
>
<div className="d-flex flex-row align-items-center text-uppercase">
{isDeleting ? (
<>
<span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
<span className="ms-2">Deleting...</span>
</>
) : (
<>
<FaTrashAlt className="me-2" />
<span>Delete</span>
</>
)}
</div>
</button>
</>
}
header={<div className={`h3 m-2 flex-grow-1 ${styles.title}`}>Delete organization</div>}
onClose={() => setOpenStatus(false)}
open={openStatus}
>
<div className="mw-100">
<div className="alert alert-warning my-4">
<span className="fw-bold text-uppercase">Important:</span> Please read this carefully.
</div>
<p>
If you delete this organization all repositories belonging to it will be deleted. Please consider
transferring them to another organization or your personal account.
</p>
<p>
All information related to the repositories will be permanently deleted as well. This includes packages,
stars, users subscriptions, webhooks, events and notifications. Some of this information was created by
users and will be lost.
</p>
<p>
<span className="fw-bold">This operation cannot be undone</span>.
</p>
<p data-testid="confirmationText">
Please type <span className="fw-bold">{props.organization.name}</span> to confirm:
</p>
<InputField type="text" name="orgName" autoComplete="off" value="" onChange={onInputChange} />
</div>
</Modal>
</>
);
}
Example #8
Source File: Card.tsx From hub with Apache License 2.0 | 4 votes |
APIKeyCard = (props: Props) => {
const [isDeleting, setIsDeleting] = useState(false);
const [dropdownMenuStatus, setDropdownMenuStatus] = useState<boolean>(false);
const dropdownMenu = useRef(null);
const [deletionModalStatus, setDeletionModalStatus] = useState<boolean>(false);
const closeDropdown = () => {
setDropdownMenuStatus(false);
};
useOutsideClick([dropdownMenu], dropdownMenuStatus, closeDropdown);
async function deleteAPIKey() {
try {
setIsDeleting(true);
await API.deleteAPIKey(props.apiKey.apiKeyId!);
setIsDeleting(false);
props.onSuccess();
} catch (err: any) {
setIsDeleting(false);
if (err.kind === ErrorKind.Unauthorized) {
props.onAuthError();
} else {
alertDispatcher.postAlert({
type: 'danger',
message: 'An error occurred deleting the API key, please try again later.',
});
}
}
}
return (
<div className="col-12 col-xxl-6 py-sm-3 py-2 px-0 px-xxl-3" data-testid="APIKeyCard">
<div className="card h-100">
<div className="card-body d-flex flex-column h-100">
<div className="d-flex flex-row w-100 justify-content-between">
<div className={`h5 mb-1 me-2 text-break ${styles.titleCard}`}>{props.apiKey.name}</div>
{deletionModalStatus && (
<Modal
className={`d-inline-block ${styles.modal}`}
closeButton={
<>
<button
className="btn btn-sm btn-outline-secondary text-uppercase"
onClick={() => setDeletionModalStatus(false)}
aria-label="Cancel"
>
<div className="d-flex flex-row align-items-center">
<IoMdCloseCircle className="me-2" />
<span>Cancel</span>
</div>
</button>
<button
className="btn btn-sm btn-danger ms-3"
onClick={(e) => {
e.preventDefault();
closeDropdown();
deleteAPIKey();
}}
disabled={isDeleting}
aria-label="Delete API key"
>
<div className="d-flex flex-row align-items-center text-uppercase">
{isDeleting ? (
<>
<span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
<span className="ms-2">Deleting...</span>
</>
) : (
<>
<FaTrashAlt className={`me-2 ${styles.btnDeleteIcon}`} />
<span>Delete</span>
</>
)}
</div>
</button>
</>
}
header={<div className={`h3 m-2 flex-grow-1 ${styles.title}`}>Delete API key</div>}
onClose={() => setDeletionModalStatus(false)}
open
>
<div className="mt-3 mw-100 text-center">
<p>Are you sure you want to remove this API key?</p>
</div>
</Modal>
)}
<div className="ms-auto">
<div
ref={dropdownMenu}
className={classnames('dropdown-menu dropdown-menu-end p-0', styles.dropdownMenu, {
show: dropdownMenuStatus,
})}
>
<div className={`dropdown-arrow ${styles.arrow}`} />
<button
className="dropdown-item btn btn-sm rounded-0 text-dark"
onClick={(e: ReactMouseEvent<HTMLButtonElement>) => {
e.preventDefault();
closeDropdown();
props.setModalStatus({
open: true,
apiKey: props.apiKey,
});
}}
aria-label="Open API key modal"
>
<div className="d-flex flex-row align-items-center">
<FaPencilAlt className={`me-2 ${styles.btnIcon}`} />
<span>Edit</span>
</div>
</button>
<button
className="dropdown-item btn btn-sm rounded-0 text-dark"
onClick={(e: ReactMouseEvent<HTMLButtonElement>) => {
e.preventDefault();
closeDropdown();
setDeletionModalStatus(true);
}}
aria-label="Open deletion modal"
>
<div className="d-flex flex-row align-items-center">
<FaTrashAlt className={`me-2 ${styles.btnIcon}`} />
<span>Delete</span>
</div>
</button>
</div>
<button
className={`btn btn-outline-secondary rounded-circle p-0 text-center ${styles.btnDropdown}`}
onClick={() => setDropdownMenuStatus(true)}
aria-label="Open menu"
aria-expanded={dropdownMenuStatus}
>
<BsThreeDotsVertical />
</button>
</div>
</div>
<div className="mt-2 d-flex flex-row align-items-baseline">
<div className="text-truncate">
<small className="text-muted text-uppercase me-1">API-KEY-ID: </small>
<small>{props.apiKey.apiKeyId}</small>
</div>
<div className={`ms-1 ${styles.copyBtn}`}>
<div className={`position-absolute ${styles.copyBtnWrapper}`}>
<ButtonCopyToClipboard
text={props.apiKey.apiKeyId!}
className="btn-link border-0 text-dark fw-bold"
label="Copy API key ID to clipboard"
/>
</div>
</div>
</div>
<div className="text-truncate">
<small className="text-muted text-uppercase me-1">Created at: </small>
<small>{moment.unix(props.apiKey.createdAt!).format('YYYY/MM/DD HH:mm:ss (Z)')}</small>
</div>
</div>
</div>
</div>
);
}
Example #9
Source File: DeleteAccount.tsx From hub with Apache License 2.0 | 4 votes |
DeleteAccount = (props: Props) => {
const { ctx } = useContext(AppCtx);
const [openStatus, setOpenStatus] = useState<boolean>(false);
const [isDeleting, setIsDeleting] = useState(false);
const [deleteSuccess, setDeleteSuccess] = useState<boolean>(false);
const [isValidInput, setIsValidInput] = useState<boolean>(false);
const onInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setIsValidInput(e.target.value === ctx.user!.alias);
};
const onClose = () => {
setOpenStatus(false);
setDeleteSuccess(false);
setIsValidInput(false);
};
async function registerDeleteUserCode() {
try {
setIsDeleting(true);
await API.registerDeleteUserCode();
setIsDeleting(false);
setDeleteSuccess(true);
} catch (err: any) {
setIsDeleting(false);
if (err.kind === ErrorKind.Unauthorized) {
props.onAuthError();
} else {
alertDispatcher.postAlert({
type: 'danger',
message: 'An error occurred deleting your account, please try again later.',
});
}
}
}
return (
<>
<div className="mt-4 mt-md-5">
<p className="mb-4">
Deleting your account will also delete all the content that belongs to it (repositories, subscriptions,
webhooks, etc), as well as all organizations where you are the only member.
</p>
<button
className="btn btn-sm btn-danger"
onClick={() => setOpenStatus(true)}
aria-label="Open deletion account modal"
>
<div className="d-flex flex-row align-items-center text-uppercase">
<FaTrashAlt className="me-2" />
<div>Delete account</div>
</div>
</button>
</div>
<Modal
className="d-inline-block"
modalClassName={styles.modal}
closeButton={
deleteSuccess ? undefined : (
<>
<button
className="btn btn-sm btn-outline-secondary text-uppercase"
onClick={() => setOpenStatus(false)}
aria-label="Close"
>
<div className="d-flex flex-row align-items-center">
<IoMdCloseCircle className="me-2" />
<span>Cancel</span>
</div>
</button>
<button
className="btn btn-sm btn-danger ms-3"
onClick={(e) => {
e.preventDefault();
registerDeleteUserCode();
}}
disabled={isDeleting || !isValidInput}
aria-label="Delete account"
>
<div className="d-flex flex-row align-items-center text-uppercase">
{isDeleting ? (
<>
<span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
<span className="ms-2">Deleting...</span>
</>
) : (
<>
<FaTrashAlt className="me-2" />
<span>Delete account</span>
</>
)}
</div>
</button>
</>
)
}
header={<div className={`h3 m-2 flex-grow-1 ${styles.title}`}>Delete account</div>}
onClose={onClose}
open={openStatus}
>
<div className="mw-100 h-100">
{deleteSuccess ? (
<div className="d-flex h-100 w-100 align-items-center justify-content-center">
<div className="alert" role="alert" aria-live="assertive" aria-atomic="true">
<div className="d-flex flex-sm-column flex-md-row align-items-center">
<div className="me-3">
<CgUserRemove className="h1 text-dark mb-3 mb-md-0" />
</div>
<h4 className="alert-heading mb-0">We've just sent you a confirmation email</h4>
</div>
<hr />
<p>
Please click on the link that has just been sent to your email account to delete your account and
complete the process.
</p>
<p className="mb-0">
Please note that the link <span className="fw-bold">is only valid for 15 minutes</span>. If you
haven't clicked the link by then you'll need to start the process from the beginning.
</p>
</div>
</div>
) : (
<>
<div className="alert alert-warning my-4">
<span className="fw-bold text-uppercase">Important:</span> Please read this carefully.
</div>
<p>
If you delete your account all repositories belonging to it will be deleted. Please consider
transferring them to another user.
</p>
<p>
All information related to the repositories will be permanently deleted as well. This includes packages,
stars, users subscriptions, webhooks, events and notifications. Some of this information was created by
users and will be lost. In addition to that, all organizations where you are the only member and all
content belonging to those organizations will be removed as well.
</p>
<p>
<span className="fw-bold">This operation cannot be undone</span>.
</p>
<p data-testid="confirmationText">
Please type <span className="fw-bold">{ctx.user!.alias}</span> to confirm:
</p>
<div className="pb-3">
<InputField type="text" name="alias" autoComplete="off" value="" onChange={onInputChange} />
</div>
</>
)}
</div>
</Modal>
</>
);
}
Example #10
Source File: DisableModal.tsx From hub with Apache License 2.0 | 4 votes |
DisableTwoFactorAuthenticationModal = (props: Props) => {
const [openStatus, setOpenStatus] = useState<boolean>(false);
const [isProcessing, setIsProcessing] = useState<boolean>(false);
const [passcode, setPasscode] = useState<string>('');
const [apiError, setApiError] = useState<null | string>(null);
const onPasscodeChange = (e: ChangeEvent<HTMLInputElement>) => {
setPasscode(e.target.value);
if (!isNull(apiError)) {
setApiError(null);
}
};
async function disableTFA() {
try {
setIsProcessing(true);
await API.disableTFA(passcode);
setApiError(null);
props.onChange();
setOpenStatus(false);
} catch (err: any) {
setIsProcessing(false);
if (err.kind !== ErrorKind.Unauthorized) {
setApiError('An error occurred turning off two-factor authentication, please try again later.');
} else {
props.onAuthError();
}
}
}
return (
<>
<button
className="btn btn-danger btn-sm"
onClick={() => setOpenStatus(true)}
aria-label="Open disable two-factor authentication modal"
>
<div className="d-flex flex-row align-items-center">
<FaUnlock className="me-2" />
<span>Disable two-factor authentication</span>
</div>
</button>
<Modal
modalClassName={styles.modal}
header={<div className={`h3 m-2 flex-grow-1 text-truncate ${styles.title}`}>Disable 2FA</div>}
open={openStatus}
onClose={() => setOpenStatus(false)}
closeButton={
<>
<button
className="btn btn-sm btn-outline-secondary text-uppercase"
onClick={() => setOpenStatus(false)}
aria-label="Cancel"
>
<div className="d-flex flex-row align-items-center">
<IoMdCloseCircle className="me-2" />
<span>Cancel</span>
</div>
</button>
<button
className="btn btn-sm btn-danger ms-3"
onClick={(e) => {
e.preventDefault();
disableTFA();
}}
disabled={passcode === '' || isProcessing}
aria-label="Disable two-factor authentication"
>
<div className="d-flex flex-row align-items-center text-uppercase">
{isProcessing ? (
<>
<span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
<span className="ms-2">Disabling...</span>
</>
) : (
<>
<FaUnlock className="me-2" />
<span>Disable</span>
</>
)}
</div>
</button>
</>
}
error={apiError}
cleanError={() => setApiError(null)}
>
<div className="mw-100">
<div className={`mb-4 ${styles.label}`}>
To disable two-factor authentication for your account please enter one of the codes from the 2FA
authentication app or one of your recovery codes.
</div>
<InputField
type="text"
name="passcode"
autoComplete="off"
value={passcode}
onChange={onPasscodeChange}
invalidText={{
default: 'This field is required',
}}
validateOnBlur
required
/>
</div>
</Modal>
</>
);
}
Example #11
Source File: EnableModal.tsx From hub with Apache License 2.0 | 4 votes |
EnableTwoFactorAuthenticationModal = (props: Props) => {
const [openStatus, setOpenStatus] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isProcessing, setIsProcessing] = useState<boolean>(false);
const [setUp, setSetUp] = useState<TwoFactorAuth | null | undefined>();
const [activeStep, setActiveStep] = useState<number>(1);
const [passcode, setPasscode] = useState<string>('');
const [apiError, setApiError] = useState<null | string>(null);
const onPasscodeChange = (e: ChangeEvent<HTMLInputElement>) => {
setPasscode(e.target.value);
if (!isNull(apiError)) {
setApiError(null);
}
};
const onClose = () => {
setOpenStatus(false);
if (activeStep === 3) {
props.onChange();
}
setSetUp(undefined);
setApiError(null);
setPasscode('');
setActiveStep(1);
};
async function setUpTFA() {
try {
setIsLoading(true);
setSetUp(await API.setUpTFA());
setOpenStatus(true);
setIsLoading(false);
} catch (err: any) {
setIsLoading(false);
setSetUp(null);
if (err.kind !== ErrorKind.Unauthorized) {
let error = compoundErrorMessage(err, 'An error occurred turning on two-factor authentication');
alertDispatcher.postAlert({
type: 'danger',
message: error,
});
} else {
props.onAuthError();
}
}
}
async function enableTFA() {
try {
setIsProcessing(true);
await API.enableTFA(passcode);
setApiError(null);
setActiveStep(3);
} catch (err: any) {
setIsProcessing(false);
if (err.kind !== ErrorKind.Unauthorized) {
let error = compoundErrorMessage(err, 'An error occurred turning on two-factor authentication');
setApiError(error);
} else {
props.onAuthError();
}
}
}
return (
<>
<button className="btn btn-success btn-sm" onClick={setUpTFA} disabled={isLoading} aria-label="Open modal">
<div className="d-flex flex-row align-items-center">
{isLoading ? (
<>
<span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
<span className="ms-2">Enabling two-factor authentication...</span>
</>
) : (
<>
<FaLock className="me-2" />
<span>Enable two-factor authentication</span>
</>
)}
</div>
</button>
<Modal
modalClassName={styles.modal}
header={<div className={`h3 m-2 flex-grow-1 text-truncate ${styles.title}`}>Setup 2FA</div>}
open={openStatus}
onClose={onClose}
closeButton={
<>
<button
className="btn btn-sm btn-outline-secondary text-uppercase"
onClick={onClose}
aria-label={activeStep === 3 ? 'Close' : 'Cancel'}
>
<div className="d-flex flex-row align-items-center">
<IoMdCloseCircle className="me-2" />
<span>{activeStep === 3 ? 'Close' : 'Cancel'}</span>
</div>
</button>
{(() => {
switch (activeStep) {
case 1:
return (
<button
className="btn btn-sm btn-outline-secondary ms-3"
onClick={(e) => {
e.preventDefault();
setActiveStep(2);
}}
aria-label="Open next step"
>
<div className="d-flex flex-row align-items-center text-uppercase">
<MdNavigateNext className="me-2" />
<span>Next</span>
</div>
</button>
);
case 2:
return (
<button
className="btn btn-sm btn-success ms-3"
onClick={(e) => {
e.preventDefault();
enableTFA();
}}
disabled={passcode === '' || isProcessing}
aria-label="Enable two-factor authentication"
>
<div className="d-flex flex-row align-items-center text-uppercase">
{isProcessing ? (
<>
<span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
<span className="ms-2">Enabling...</span>
</>
) : (
<>
<FaLock className="me-2" />
<span>Enable</span>
</>
)}
</div>
</button>
);
default:
return null;
}
})()}
</>
}
error={apiError}
cleanError={() => setApiError(null)}
>
<div className="mw-100 h-100 position-relative">
{setUp && (
<>
{(() => {
switch (activeStep) {
case 1:
return (
<>
<div className="h4">Recovery codes</div>
<div className={`mt-3 mb-4 ${styles.label}`}>
These codes can be used if you lose access to your 2FA credentials. Each of the codes can only
be used once.{' '}
<span className="fw-bold">Please treat them as passwords and store them safely</span>.
</div>
<div className={`border rounded position-relative p-2 p-sm-4 ${styles.codesWrapper}`}>
<BlockCodeButtons
filename="artifacthub-recovery-codes.txt"
content={setUp.recoveryCodes.join('\n')}
hiddenCopyBtn
/>
<div className="d-flex flex-column align-items-center overflow-auto">
{setUp.recoveryCodes.map((code: string) => (
<div className={`font-monospace ${styles.code}`} key={`code_${code}`}>
{code}
</div>
))}
</div>
</div>
<div className="mt-4 alert alert-warning">
We strongly recommend you to download and print your recovery codes before proceeding with the
2FA setup.
</div>
</>
);
case 2:
return (
<>
<div className="h4">Authentication app</div>
<div className={`mt-3 mb-4 ${styles.label}`}>
Please scan the image below with your 2FA authentication app. If you can't scan it, you can
use{' '}
<ButtonCopyToClipboard
text={setUp.secret}
wrapperClassName="d-inline-block"
icon={<></>}
visibleBtnText
contentBtn="this text code"
className={`btn-link text-reset p-0 text-secondary fw-bold ${styles.copyBtn}`}
label="Copy 2FA code to clipboard"
/>
to set it up manually.
</div>
<div className="text-center mb-4">
<div className="border rounded d-inline-block p-1 my-1">
<img className={styles.qrCode} src={setUp.qrCode} alt="QR code" />
</div>
</div>
<div className={`mb-4 ${styles.label}`}>
Please enter one of the codes from the 2FA authentication app to confirm your account has been
setup successfully before completing the process.
</div>
<InputField
className={styles.inputWrapper}
type="text"
name="passcode"
autoComplete="off"
value={passcode}
onChange={onPasscodeChange}
invalidText={{
default: 'This field is required',
}}
validateOnBlur
required
/>
</>
);
case 3:
return (
<div className="d-flex flex-column h-100 w-100 px-3 align-items-center justify-content-center text-center position-relative">
{isNull(apiError) && (
<>
<MdDone className="display-4 text-success mb-4" />
Two-factor authentication has been successfully enabled. We recommend you to sign out and
back in to your account.
</>
)}
</div>
);
default:
return null;
}
})()}
</>
)}
</div>
</Modal>
</>
);
}
Example #12
Source File: Card.tsx From hub with Apache License 2.0 | 4 votes |
WebhookCard = (props: Props) => {
const { ctx } = useContext(AppCtx);
const [isDeleting, setIsDeleting] = useState<boolean>(false);
const [dropdownMenuStatus, setDropdownMenuStatus] = useState<boolean>(false);
const dropdownMenu = useRef(null);
const [deletionModalStatus, setDeletionModalStatus] = useState<boolean>(false);
const closeDropdown = () => {
setDropdownMenuStatus(false);
};
useOutsideClick([dropdownMenu], dropdownMenuStatus, closeDropdown);
async function deleteWebhook() {
try {
setIsDeleting(true);
await API.deleteWebhook(props.webhook.webhookId!, ctx.prefs.controlPanel.selectedOrg);
setIsDeleting(false);
props.onDeletion();
} catch (err: any) {
setIsDeleting(false);
if (err.kind === ErrorKind.Unauthorized) {
props.onAuthError();
} else {
alertDispatcher.postAlert({
type: 'danger',
message: 'An error occurred deleting the webhook, please try again later.',
});
}
}
}
return (
<div className="col-12 col-xxl-6 py-sm-3 py-2 px-0 px-xxl-3" role="listitem">
<div className={`card cardWithHover w-100 h-100 mw-100 bg-white ${styles.card}`}>
<div className="card-body position-relative">
<div className="d-flex flex-row">
<div className="h5 card-title mb-3 me-3 lh-1 text-break">
<div className="d-flex flex-row align-items-start">
<div>{props.webhook.name}</div>
{props.webhook.active ? (
<span
className={`ms-3 mt-1 fw-bold badge rounded-pill border border-success text-success text-uppercase ${styles.badge}`}
>
Active
</span>
) : (
<span
className={`ms-3 mt-1 fw-bold badge rounded-pill border border-dark text-dark text-uppercase ${styles.badge} ${styles.inactiveBadge}`}
>
Inactive
</span>
)}
</div>
</div>
{deletionModalStatus && (
<Modal
className={`d-inline-block ${styles.modal}`}
closeButton={
<>
<button
className="btn btn-sm btn-outline-secondary text-uppercase"
onClick={() => setDeletionModalStatus(false)}
aria-label="Close deletion modal"
>
<div className="d-flex flex-row align-items-center">
<IoMdCloseCircle className="me-2" />
<span>Cancel</span>
</div>
</button>
<button
className="btn btn-sm btn-danger ms-3"
onClick={(e) => {
e.preventDefault();
deleteWebhook();
}}
disabled={isDeleting}
aria-label="Delete webhook"
>
<div className="d-flex flex-row align-items-center text-uppercase">
{isDeleting ? (
<>
<span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
<span className="ms-2">Deleting...</span>
</>
) : (
<>
<FaTrashAlt className={`me-2 ${styles.btnDeleteIcon}`} />
<span>Delete</span>
</>
)}
</div>
</button>
</>
}
header={<div className={`h3 m-2 flex-grow-1 ${styles.title}`}>Delete webhook</div>}
onClose={() => setDeletionModalStatus(false)}
open
>
<div className="mt-3 mw-100 text-center">
<p>Are you sure you want to delete this webhook?</p>
</div>
</Modal>
)}
<div className="ms-auto">
<div
ref={dropdownMenu}
className={classnames('dropdown-menu dropdown-menu-end p-0', styles.dropdownMenu, {
show: dropdownMenuStatus,
})}
>
<div className={`dropdown-arrow ${styles.arrow}`} />
<button
className="dropdown-item btn btn-sm rounded-0 text-dark"
onClick={(e: ReactMouseEvent<HTMLButtonElement>) => {
e.preventDefault();
closeDropdown();
props.onEdition();
}}
aria-label="Edit webhook"
>
<div className="d-flex flex-row align-items-center">
<FaPencilAlt className={`me-2 ${styles.btnIcon}`} />
<span>Edit</span>
</div>
</button>
<button
className="dropdown-item btn btn-sm rounded-0 text-dark"
onClick={(e: ReactMouseEvent<HTMLButtonElement>) => {
e.preventDefault();
closeDropdown();
setDeletionModalStatus(true);
}}
aria-label="Open deletion webhook modal"
>
<div className="d-flex flex-row align-items-center">
<FaTrashAlt className={`me-2 ${styles.btnIcon}`} />
<span>Delete</span>
</div>
</button>
</div>
<button
className={`btn btn-outline-secondary rounded-circle p-0 text-center ${styles.btnDropdown}`}
onClick={() => setDropdownMenuStatus(true)}
aria-label="Open menu"
aria-expanded={dropdownMenuStatus}
>
<BsThreeDotsVertical />
</button>
</div>
</div>
<div className="d-flex flex-column">
<div className="card-subtitle d-flex flex-column mw-100 mt-1">
<p className="card-text">{props.webhook.description}</p>
</div>
<div className="text-truncate">
<small className="text-muted text-uppercase me-2">Url:</small>
<small>{props.webhook.url}</small>
</div>
<div className="d-flex flex-row justify-content-between align-items-baseline">
{props.webhook.lastNotifications && (
<div className="d-none d-md-inline mt-2">
<LastNotificationsModal notifications={props.webhook.lastNotifications} />
</div>
)}
{(isUndefined(props.webhook.packages) || props.webhook.packages.length === 0) && (
<div className="ms-auto mt-2">
<ElementWithTooltip
element={
<span
className={`d-flex flex-row align-items-center badge bg-warning rounded-pill ${styles.badgeNoPackages}`}
>
<TiWarningOutline />
<span className="ms-1">No packages</span>
</span>
}
tooltipMessage="This webhook is not associated to any packages."
active
visibleTooltip
/>
</div>
)}
</div>
</div>
</div>
</div>
</div>
);
}