fix: Cleanup user menu (#4532)
Move confirmation dialogs from window confirmations
This commit is contained in:
@@ -252,8 +252,9 @@ const Small = styled.div`
|
|||||||
animation: ${fadeAndScaleIn} 250ms ease;
|
animation: ${fadeAndScaleIn} 250ms ease;
|
||||||
|
|
||||||
margin: auto auto;
|
margin: auto auto;
|
||||||
|
width: 30vw;
|
||||||
min-width: 350px;
|
min-width: 350px;
|
||||||
max-width: 30vw;
|
max-width: 450px;
|
||||||
z-index: ${depths.modal};
|
z-index: ${depths.modal};
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
108
app/components/UserRoleDialogs.tsx
Normal file
108
app/components/UserRoleDialogs.tsx
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import User from "~/models/User";
|
||||||
|
import ConfirmationDialog from "~/components/ConfirmationDialog";
|
||||||
|
import useStores from "~/hooks/useStores";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
user: User;
|
||||||
|
onSubmit: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function UserChangeToViewerDialog({ user, onSubmit }: Props) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { users } = useStores();
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await users.demote(user, "viewer");
|
||||||
|
onSubmit();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmationDialog
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
submitText={t("Confirm")}
|
||||||
|
savingText={`${t("Saving")}…`}
|
||||||
|
>
|
||||||
|
{t(
|
||||||
|
"Are you sure you want to make {{ userName }} a read-only viewer? They will not be able to edit any content",
|
||||||
|
{
|
||||||
|
userName: user.name,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
.
|
||||||
|
</ConfirmationDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UserChangeToMemberDialog({ user, onSubmit }: Props) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { users } = useStores();
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await users.demote(user, "member");
|
||||||
|
onSubmit();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmationDialog
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
submitText={t("Confirm")}
|
||||||
|
savingText={`${t("Saving")}…`}
|
||||||
|
>
|
||||||
|
{t("Are you sure you want to make {{ userName }} a member?", {
|
||||||
|
userName: user.name,
|
||||||
|
})}
|
||||||
|
</ConfirmationDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UserChangeToAdminDialog({ user, onSubmit }: Props) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { users } = useStores();
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await users.promote(user);
|
||||||
|
onSubmit();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmationDialog
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
submitText={t("Confirm")}
|
||||||
|
savingText={`${t("Saving")}…`}
|
||||||
|
>
|
||||||
|
{t(
|
||||||
|
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.",
|
||||||
|
{
|
||||||
|
userName: user.name,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</ConfirmationDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UserSuspendDialog({ user, onSubmit }: Props) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { users } = useStores();
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await users.suspend(user);
|
||||||
|
onSubmit();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmationDialog
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
submitText={t("Confirm")}
|
||||||
|
savingText={`${t("Saving")}…`}
|
||||||
|
>
|
||||||
|
{t(
|
||||||
|
"Are you sure you want to suspend {{ userName }}? Suspended users will be prevented from logging in.",
|
||||||
|
{
|
||||||
|
userName: user.name,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</ConfirmationDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -6,6 +6,12 @@ import User from "~/models/User";
|
|||||||
import ContextMenu from "~/components/ContextMenu";
|
import ContextMenu from "~/components/ContextMenu";
|
||||||
import OverflowMenuButton from "~/components/ContextMenu/OverflowMenuButton";
|
import OverflowMenuButton from "~/components/ContextMenu/OverflowMenuButton";
|
||||||
import Template from "~/components/ContextMenu/Template";
|
import Template from "~/components/ContextMenu/Template";
|
||||||
|
import {
|
||||||
|
UserChangeToAdminDialog,
|
||||||
|
UserChangeToMemberDialog,
|
||||||
|
UserChangeToViewerDialog,
|
||||||
|
UserSuspendDialog,
|
||||||
|
} from "~/components/UserRoleDialogs";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useToasts from "~/hooks/useToasts";
|
import useToasts from "~/hooks/useToasts";
|
||||||
@@ -15,7 +21,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function UserMenu({ user }: Props) {
|
function UserMenu({ user }: Props) {
|
||||||
const { users } = useStores();
|
const { users, dialogs } = useStores();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const menu = useMenuState({
|
const menu = useMenuState({
|
||||||
modal: true,
|
modal: true,
|
||||||
@@ -26,83 +32,66 @@ function UserMenu({ user }: Props) {
|
|||||||
const handlePromote = React.useCallback(
|
const handlePromote = React.useCallback(
|
||||||
(ev: React.SyntheticEvent) => {
|
(ev: React.SyntheticEvent) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
dialogs.openModal({
|
||||||
if (
|
title: t("Change role to admin"),
|
||||||
!window.confirm(
|
isCentered: true,
|
||||||
t(
|
content: (
|
||||||
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.",
|
<UserChangeToAdminDialog
|
||||||
{
|
user={user}
|
||||||
userName: user.name,
|
onSubmit={dialogs.closeAllModals}
|
||||||
}
|
/>
|
||||||
)
|
),
|
||||||
)
|
});
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
users.promote(user);
|
|
||||||
},
|
},
|
||||||
[users, user, t]
|
[dialogs, t, user]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleMember = React.useCallback(
|
const handleMember = React.useCallback(
|
||||||
(ev: React.SyntheticEvent) => {
|
(ev: React.SyntheticEvent) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
dialogs.openModal({
|
||||||
if (
|
title: t("Change role to member"),
|
||||||
!window.confirm(
|
isCentered: true,
|
||||||
t("Are you sure you want to make {{ userName }} a member?", {
|
content: (
|
||||||
userName: user.name,
|
<UserChangeToMemberDialog
|
||||||
})
|
user={user}
|
||||||
)
|
onSubmit={dialogs.closeAllModals}
|
||||||
) {
|
/>
|
||||||
return;
|
),
|
||||||
}
|
});
|
||||||
|
|
||||||
users.demote(user, "member");
|
|
||||||
},
|
},
|
||||||
[users, user, t]
|
[dialogs, t, user]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleViewer = React.useCallback(
|
const handleViewer = React.useCallback(
|
||||||
(ev: React.SyntheticEvent) => {
|
(ev: React.SyntheticEvent) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
dialogs.openModal({
|
||||||
if (
|
title: t("Change role to viewer"),
|
||||||
!window.confirm(
|
isCentered: true,
|
||||||
t(
|
content: (
|
||||||
"Are you sure you want to make {{ userName }} a read-only viewer? They will not be able to edit any content",
|
<UserChangeToViewerDialog
|
||||||
{
|
user={user}
|
||||||
userName: user.name,
|
onSubmit={dialogs.closeAllModals}
|
||||||
}
|
/>
|
||||||
)
|
),
|
||||||
)
|
});
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
users.demote(user, "viewer");
|
|
||||||
},
|
},
|
||||||
[users, user, t]
|
[dialogs, t, user]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSuspend = React.useCallback(
|
const handleSuspend = React.useCallback(
|
||||||
(ev: React.SyntheticEvent) => {
|
(ev: React.SyntheticEvent) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
dialogs.openModal({
|
||||||
if (
|
title: t("Suspend account"),
|
||||||
!window.confirm(
|
isCentered: true,
|
||||||
t(
|
content: (
|
||||||
"Are you sure you want to suspend this account? Suspended users will be prevented from logging in."
|
<UserSuspendDialog user={user} onSubmit={dialogs.closeAllModals} />
|
||||||
)
|
),
|
||||||
)
|
});
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
users.suspend(user);
|
|
||||||
},
|
},
|
||||||
[users, user, t]
|
[dialogs, t, user]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleRevoke = React.useCallback(
|
const handleRevoke = React.useCallback(
|
||||||
@@ -149,25 +138,19 @@ function UserMenu({ user }: Props) {
|
|||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
type: "button",
|
type: "button",
|
||||||
title: t("Make {{ userName }} a member", {
|
title: `${t("Change role to member")}…`,
|
||||||
userName: user.name,
|
|
||||||
}),
|
|
||||||
onClick: handleMember,
|
onClick: handleMember,
|
||||||
visible: can.demote && user.role !== "member",
|
visible: can.demote && user.role !== "member",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "button",
|
type: "button",
|
||||||
title: t("Make {{ userName }} a viewer", {
|
title: `${t("Change role to viewer")}…`,
|
||||||
userName: user.name,
|
|
||||||
}),
|
|
||||||
onClick: handleViewer,
|
onClick: handleViewer,
|
||||||
visible: can.demote && user.role !== "viewer",
|
visible: can.demote && user.role !== "viewer",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "button",
|
type: "button",
|
||||||
title: t("Make {{ userName }} an admin…", {
|
title: `${t("Change role to admin")}…`,
|
||||||
userName: user.name,
|
|
||||||
}),
|
|
||||||
onClick: handlePromote,
|
onClick: handlePromote,
|
||||||
visible: can.promote && user.role !== "admin",
|
visible: can.promote && user.role !== "admin",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -205,6 +205,12 @@
|
|||||||
"No results": "No results",
|
"No results": "No results",
|
||||||
"Previous page": "Previous page",
|
"Previous page": "Previous page",
|
||||||
"Next page": "Next page",
|
"Next page": "Next page",
|
||||||
|
"Confirm": "Confirm",
|
||||||
|
"Saving": "Saving",
|
||||||
|
"Are you sure you want to make {{ userName }} a read-only viewer? They will not be able to edit any content": "Are you sure you want to make {{ userName }} a read-only viewer? They will not be able to edit any content",
|
||||||
|
"Are you sure you want to make {{ userName }} a member?": "Are you sure you want to make {{ userName }} a member?",
|
||||||
|
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.",
|
||||||
|
"Are you sure you want to suspend {{ userName }}? Suspended users will be prevented from logging in.": "Are you sure you want to suspend {{ userName }}? Suspended users will be prevented from logging in.",
|
||||||
"Account": "Account",
|
"Account": "Account",
|
||||||
"Notifications": "Notifications",
|
"Notifications": "Notifications",
|
||||||
"API Tokens": "API Tokens",
|
"API Tokens": "API Tokens",
|
||||||
@@ -326,19 +332,15 @@
|
|||||||
"Headings you add to the document will appear here": "Headings you add to the document will appear here",
|
"Headings you add to the document will appear here": "Headings you add to the document will appear here",
|
||||||
"Table of contents": "Table of contents",
|
"Table of contents": "Table of contents",
|
||||||
"By {{ author }}": "By {{ author }}",
|
"By {{ author }}": "By {{ author }}",
|
||||||
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.",
|
"Change role to admin": "Change role to admin",
|
||||||
"Are you sure you want to make {{ userName }} a member?": "Are you sure you want to make {{ userName }} a member?",
|
"Change role to member": "Change role to member",
|
||||||
"Are you sure you want to make {{ userName }} a read-only viewer? They will not be able to edit any content": "Are you sure you want to make {{ userName }} a read-only viewer? They will not be able to edit any content",
|
"Change role to viewer": "Change role to viewer",
|
||||||
"Are you sure you want to suspend this account? Suspended users will be prevented from logging in.": "Are you sure you want to suspend this account? Suspended users will be prevented from logging in.",
|
"Suspend account": "Suspend account",
|
||||||
"An error occurred while sending the invite": "An error occurred while sending the invite",
|
"An error occurred while sending the invite": "An error occurred while sending the invite",
|
||||||
"User options": "User options",
|
"User options": "User options",
|
||||||
"Make {{ userName }} a member": "Make {{ userName }} a member",
|
|
||||||
"Make {{ userName }} a viewer": "Make {{ userName }} a viewer",
|
|
||||||
"Make {{ userName }} an admin…": "Make {{ userName }} an admin…",
|
|
||||||
"Resend invite": "Resend invite",
|
"Resend invite": "Resend invite",
|
||||||
"Revoke invite": "Revoke invite",
|
"Revoke invite": "Revoke invite",
|
||||||
"Activate account": "Activate account",
|
"Activate account": "Activate account",
|
||||||
"Suspend account": "Suspend account",
|
|
||||||
"API token created": "API token created",
|
"API token created": "API token created",
|
||||||
"Name your token something that will help you to remember it's use in the future, for example \"local development\", \"production\", or \"continuous integration\".": "Name your token something that will help you to remember it's use in the future, for example \"local development\", \"production\", or \"continuous integration\".",
|
"Name your token something that will help you to remember it's use in the future, for example \"local development\", \"production\", or \"continuous integration\".": "Name your token something that will help you to remember it's use in the future, for example \"local development\", \"production\", or \"continuous integration\".",
|
||||||
"The document archive is empty at the moment.": "The document archive is empty at the moment.",
|
"The document archive is empty at the moment.": "The document archive is empty at the moment.",
|
||||||
@@ -367,7 +369,6 @@
|
|||||||
"You can edit the name and other details at any time, however doing so often might confuse your team mates.": "You can edit the name and other details at any time, however doing so often might confuse your team mates.",
|
"You can edit the name and other details at any time, however doing so often might confuse your team mates.": "You can edit the name and other details at any time, however doing so often might confuse your team mates.",
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
"Sort": "Sort",
|
"Sort": "Sort",
|
||||||
"Saving": "Saving",
|
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
"Export started. If you have notifications enabled, you will receive an email when it's complete.": "Export started. If you have notifications enabled, you will receive an email when it's complete.",
|
"Export started. If you have notifications enabled, you will receive an email when it's complete.": "Export started. If you have notifications enabled, you will receive an email when it's complete.",
|
||||||
"Exporting the collection <em>{{collectionName}}</em> may take some time.": "Exporting the collection <em>{{collectionName}}</em> may take some time.",
|
"Exporting the collection <em>{{collectionName}}</em> may take some time.": "Exporting the collection <em>{{collectionName}}</em> may take some time.",
|
||||||
|
|||||||
Reference in New Issue
Block a user