diff --git a/backend/models/token.js b/backend/models/token.js index fb40e2a0..81a8700b 100644 --- a/backend/models/token.js +++ b/backend/models/token.js @@ -13,7 +13,7 @@ import { global as logger } from "../logger.js"; const ALGO = "RS256"; export default () => { - let token_data = {}; + let tokenData = {}; const self = { /** @@ -37,7 +37,7 @@ export default () => { if (err) { reject(err); } else { - token_data = payload; + tokenData = payload; resolve({ token: token, payload: payload, @@ -72,18 +72,18 @@ export default () => { reject(err); } } else { - token_data = result; + tokenData = result; // Hack: some tokens out in the wild have a scope of 'all' instead of 'user'. // For 30 days at least, we need to replace 'all' with user. if ( - typeof token_data.scope !== "undefined" && - _.indexOf(token_data.scope, "all") !== -1 + typeof tokenData.scope !== "undefined" && + _.indexOf(tokenData.scope, "all") !== -1 ) { - token_data.scope = ["user"]; + tokenData.scope = ["user"]; } - resolve(token_data); + resolve(tokenData); } }, ); @@ -100,15 +100,15 @@ export default () => { * @param {String} scope * @returns {Boolean} */ - hasScope: (scope) => typeof token_data.scope !== "undefined" && _.indexOf(token_data.scope, scope) !== -1, + hasScope: (scope) => typeof tokenData.scope !== "undefined" && _.indexOf(tokenData.scope, scope) !== -1, /** * @param {String} key * @return {*} */ get: (key) => { - if (typeof token_data[key] !== "undefined") { - return token_data[key]; + if (typeof tokenData[key] !== "undefined") { + return tokenData[key]; } return null; @@ -119,7 +119,7 @@ export default () => { * @param {*} value */ set: (key, value) => { - token_data[key] = value; + tokenData[key] = value; }, /** diff --git a/frontend/src/components/SiteHeader.tsx b/frontend/src/components/SiteHeader.tsx index f3491fc3..d60e26e5 100644 --- a/frontend/src/components/SiteHeader.tsx +++ b/frontend/src/components/SiteHeader.tsx @@ -66,7 +66,9 @@ export function SiteHeader() {
{currentUser?.nickname}
- {intl.formatMessage({ id: isAdmin ? "administrator" : "standard-user" })} + {intl.formatMessage({ + id: isAdmin ? "role.admin" : "role.standard-user", + })}
diff --git a/frontend/src/components/Table/Formatter/DomainsFormatter.tsx b/frontend/src/components/Table/Formatter/DomainsFormatter.tsx index 5ab339f2..daa7f08e 100644 --- a/frontend/src/components/Table/Formatter/DomainsFormatter.tsx +++ b/frontend/src/components/Table/Formatter/DomainsFormatter.tsx @@ -10,9 +10,9 @@ export function DomainsFormatter({ domains, createdOn }: Props) {
{domains.map((domain: string) => ( - + {domain} - + ))}
{createdOn ? ( diff --git a/frontend/src/components/Table/Formatter/EmailFormatter.tsx b/frontend/src/components/Table/Formatter/EmailFormatter.tsx new file mode 100644 index 00000000..3371c66a --- /dev/null +++ b/frontend/src/components/Table/Formatter/EmailFormatter.tsx @@ -0,0 +1,10 @@ +interface Props { + email: string; +} +export function EmailFormatter({ email }: Props) { + return ( + + {email} + + ); +} diff --git a/frontend/src/components/Table/Formatter/RolesFormatter.tsx b/frontend/src/components/Table/Formatter/RolesFormatter.tsx new file mode 100644 index 00000000..a2464863 --- /dev/null +++ b/frontend/src/components/Table/Formatter/RolesFormatter.tsx @@ -0,0 +1,20 @@ +import { intl } from "src/locale"; + +interface Props { + roles: string[]; +} +export function RolesFormatter({ roles }: Props) { + const r = roles || []; + if (r.length === 0) { + r[0] = "standard-user"; + } + return ( + <> + {r.map((role: string) => ( + + {intl.formatMessage({ id: `role.${role}` })} + + ))} + + ); +} diff --git a/frontend/src/components/Table/Formatter/ValueWithDateFormatter.tsx b/frontend/src/components/Table/Formatter/ValueWithDateFormatter.tsx index 0754c633..db8d60ef 100644 --- a/frontend/src/components/Table/Formatter/ValueWithDateFormatter.tsx +++ b/frontend/src/components/Table/Formatter/ValueWithDateFormatter.tsx @@ -4,16 +4,19 @@ import { intl } from "src/locale"; interface Props { value: string; createdOn?: string; + disabled?: boolean; } -export function ValueWithDateFormatter({ value, createdOn }: Props) { +export function ValueWithDateFormatter({ value, createdOn, disabled }: Props) { return (
-
{value}
+
{value}
{createdOn ? ( -
- {intl.formatMessage({ id: "created-on" }, { date: intlFormat(parseISO(createdOn)) })} +
+ {disabled + ? intl.formatMessage({ id: "disabled" }) + : intl.formatMessage({ id: "created-on" }, { date: intlFormat(parseISO(createdOn)) })}
) : null}
diff --git a/frontend/src/components/Table/Formatter/index.ts b/frontend/src/components/Table/Formatter/index.ts index 2d67aa2e..60bcbf73 100644 --- a/frontend/src/components/Table/Formatter/index.ts +++ b/frontend/src/components/Table/Formatter/index.ts @@ -1,5 +1,7 @@ export * from "./CertificateFormatter"; export * from "./DomainsFormatter"; +export * from "./EmailFormatter"; export * from "./GravatarFormatter"; +export * from "./RolesFormatter"; export * from "./StatusFormatter"; export * from "./ValueWithDateFormatter"; diff --git a/frontend/src/locale/lang/en.json b/frontend/src/locale/lang/en.json index f59a6d4d..d71431d1 100644 --- a/frontend/src/locale/lang/en.json +++ b/frontend/src/locale/lang/en.json @@ -12,7 +12,6 @@ "action.edit": "Edit", "action.enable": "Enable", "action.permissions": "Permissions", - "administrator": "Administrator", "auditlog.title": "Audit Log", "cancel": "Cancel", "certificates.title": "SSL Certificates", @@ -37,6 +36,7 @@ "dead-hosts.count": "{count} 404 Hosts", "dead-hosts.empty": "There are no 404 Hosts", "dead-hosts.title": "404 Hosts", + "disabled": "Disabled", "email-address": "Email address", "empty-subtitle": "Why don't you create one?", "error.invalid-auth": "Invalid email or password", @@ -53,6 +53,7 @@ "notfound.title": "Oops… You just found an error page", "notification.error": "Error", "notification.success": "Success", + "notification.user-deleted": "User has been deleted", "notification.user-saved": "User has been saved", "offline": "Offline", "online": "Online", @@ -67,10 +68,11 @@ "redirection-hosts.count": "{count} Redirection Hosts", "redirection-hosts.empty": "There are no Redirection Hosts", "redirection-hosts.title": "Redirection Hosts", + "role.admin": "Administrator", + "role.standard-user": "Standard User", "save": "Save", "settings.title": "Settings", "sign-in": "Sign in", - "standard-user": "Apache Helicopter", "streams.actions-title": "Stream #{id}", "streams.add": "Add Stream", "streams.count": "{count} Streams", @@ -81,8 +83,11 @@ "user.change-password": "Change Password", "user.confirm-password": "Confirm Password", "user.current-password": "Current Password", + "user.delete.content": "Are you sure you want to delete this user?", + "user.delete.title": "Delete User", "user.edit": "Edit User", "user.edit-profile": "Edit Profile", + "user.flags.title": "Properties", "user.full-name": "Full Name", "user.logout": "Logout", "user.new": "New User", diff --git a/frontend/src/locale/src/en.json b/frontend/src/locale/src/en.json index d37553e9..dd20f393 100644 --- a/frontend/src/locale/src/en.json +++ b/frontend/src/locale/src/en.json @@ -38,9 +38,6 @@ "action.permissions": { "defaultMessage": "Permissions" }, - "administrator": { - "defaultMessage": "Administrator" - }, "auditlog.title": { "defaultMessage": "Audit Log" }, @@ -113,6 +110,9 @@ "dead-hosts.title": { "defaultMessage": "404 Hosts" }, + "disabled": { + "defaultMessage": "Disabled" + }, "email-address": { "defaultMessage": "Email address" }, @@ -158,6 +158,9 @@ "notification.error": { "defaultMessage": "Error" }, + "notification.user-deleted": { + "defaultMessage": "User has been deleted" + }, "notification.user-saved": { "defaultMessage": "User has been saved" }, @@ -203,6 +206,12 @@ "redirection-hosts.title": { "defaultMessage": "Redirection Hosts" }, + "role.admin": { + "defaultMessage": "Administrator" + }, + "role.standard-user": { + "defaultMessage": "Standard User" + }, "save": { "defaultMessage": "Save" }, @@ -212,9 +221,6 @@ "sign-in": { "defaultMessage": "Sign in" }, - "standard-user": { - "defaultMessage": "Apache Helicopter" - }, "streams.actions-title": { "defaultMessage": "Stream #{id}" }, @@ -245,12 +251,21 @@ "user.current-password": { "defaultMessage": "Current Password" }, + "user.delete.title": { + "defaultMessage": "Delete User" + }, + "user.delete.content": { + "defaultMessage": "Are you sure you want to delete this user?" + }, "user.edit": { "defaultMessage": "Edit User" }, "user.edit-profile": { "defaultMessage": "Edit Profile" }, + "user.flags.title": { + "defaultMessage": "Properties" + }, "user.full-name": { "defaultMessage": "Full Name" }, diff --git a/frontend/src/modals/DeleteConfirmModal.tsx b/frontend/src/modals/DeleteConfirmModal.tsx new file mode 100644 index 00000000..fee9e046 --- /dev/null +++ b/frontend/src/modals/DeleteConfirmModal.tsx @@ -0,0 +1,65 @@ +import { useQueryClient } from "@tanstack/react-query"; +import { type ReactNode, useState } from "react"; +import { Alert } from "react-bootstrap"; +import Modal from "react-bootstrap/Modal"; +import { Button } from "src/components"; +import { intl } from "src/locale"; + +interface Props { + title: string; + children: ReactNode; + onConfirm: () => Promise | void; + onClose: () => void; + invalidations?: any[]; +} +export function DeleteConfirmModal({ title, children, onConfirm, onClose, invalidations }: Props) { + const queryClient = useQueryClient(); + const [error, setError] = useState(null); + const [submitting, setSubmitting] = useState(false); + + const onSubmit = async () => { + setSubmitting(true); + setError(null); + try { + await onConfirm(); + onClose(); + // invalidate caches as requested + invalidations?.forEach((inv) => { + queryClient.invalidateQueries({ queryKey: inv }); + }); + } catch (err: any) { + setError(intl.formatMessage({ id: err.message })); + } + setSubmitting(false); + }; + + return ( + + + {title} + + + setError(null)} dismissible> + {error} + + {children} + + + + + + + ); +} diff --git a/frontend/src/modals/UserModal.tsx b/frontend/src/modals/UserModal.tsx index 293b2ae9..b7131a85 100644 --- a/frontend/src/modals/UserModal.tsx +++ b/frontend/src/modals/UserModal.tsx @@ -18,11 +18,6 @@ export function UserModal({ userId, onClose }: Props) { const { mutate: setUser } = useSetUser(); const [errorMsg, setErrorMsg] = useState(null); - if (data && currentUser) { - console.log("DATA:", data); - console.log("CURRENT:", currentUser); - } - const onSubmit = async (values: any, { setSubmitting }: any) => { setErrorMsg(null); const { ...payload } = { @@ -161,12 +156,13 @@ export function UserModal({ userId, onClose }: Props) {
{currentUser && data && currentUser?.id !== data?.id ? (
-

Properties

- +

{intl.formatMessage({ id: "user.flags.title" })}