improvements: address feedback

This commit is contained in:
Scott Wilson
2025-09-02 12:44:02 -07:00
parent 394538769b
commit 4d6e12d6b2
10 changed files with 59 additions and 42 deletions

View File

@@ -850,7 +850,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
schema: {
body: z.object({
name: GenericResourceNameSchema,
inviteAdminEmails: z.string().email().array()
inviteAdminEmails: z.string().email().array().min(1)
}),
response: {
200: z.object({

View File

@@ -9,7 +9,6 @@ interface OrganizationAssignmentTemplateProps extends Omit<BaseEmailWrapperProps
inviterFirstName?: string;
inviterUsername?: string;
organizationName: string;
email: string;
callback_url: string;
}

View File

@@ -75,7 +75,7 @@ type TSuperAdminServiceFactoryDep = {
kmsRootConfigDAL: TKmsRootConfigDALFactory;
orgService: Pick<TOrgServiceFactory, "createOrganization" | "inviteUserToOrganization">;
keyStore: Pick<TKeyStoreFactory, "getItem" | "setItemWithExpiry" | "deleteItem" | "deleteItems">;
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures">;
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures" | "updateSubscriptionOrgMemberCount">;
microsoftTeamsService: Pick<TMicrosoftTeamsServiceFactory, "initializeTeamsBot">;
invalidateCacheQueue: TInvalidateCacheQueueFactory;
smtpService: Pick<TSmtpService, "sendMail">;
@@ -811,13 +811,13 @@ export const superAdminServiceFactory = ({
}
const inviteeUserId = inviteeUser?.id;
const existingEncrytionKey = await userDAL.findUserEncKeyByUserId(inviteeUserId, tx);
const existingEncryptionKey = await userDAL.findUserEncKeyByUserId(inviteeUserId, tx);
// when user is missing the encrytion keys
// this could happen either if user doesn't exist or user didn't find step 3 of generating the encryption keys of srp
// So what we do is we generate a random secure password and then encrypt it with a random pub-private key
// Then when user sign in (as login is not possible as isAccepted is false) we rencrypt the private key with the user password
if (!inviteeUser || (inviteeUser && !inviteeUser?.isAccepted && !existingEncrytionKey)) {
if (!inviteeUser || (inviteeUser && !inviteeUser?.isAccepted && !existingEncryptionKey)) {
await userDAL.createUserEncryption(
{
userId: inviteeUserId,
@@ -853,6 +853,8 @@ export const superAdminServiceFactory = ({
return { organization: org, users };
});
await licenseService.updateSubscriptionOrgMemberCount(organization.id);
await Promise.allSettled(
usersToEmail.map(async (user) => {
if (!user.email) return;

View File

@@ -1,3 +1,5 @@
import { OrgMembershipStatus } from "@app/hooks/api/organization/types";
import { Organization } from "../types";
export enum LoginMethod {
@@ -20,7 +22,7 @@ export type OrganizationWithProjects = Organization & {
lastName: string | null;
};
membershipId: string;
status: string;
status: OrgMembershipStatus;
role: string;
roleId: string | null;
}[];

View File

@@ -159,3 +159,8 @@ export enum OrgIdentityOrderBy {
Name = "name",
Role = "role"
}
export enum OrgMembershipStatus {
Invited = "invited",
Accepted = "accepted"
}

View File

@@ -192,14 +192,18 @@ export const Navbar = () => {
<p className="pl-1 pr-3 text-lg text-mineshaft-400/70">/</p>
{isServerAdminPanel ? (
<>
<div className="group flex cursor-pointer items-center gap-2 text-sm text-white transition-all duration-100 hover:text-primary">
<Link
to="/admin"
className="group flex cursor-pointer items-center gap-2 text-sm text-white transition-all duration-100 hover:text-primary"
>
<div>
<FontAwesomeIcon icon={faServer} className="text-xs text-bunker-300" />
</div>
<div className="whitespace-nowrap">Server Console</div>
</div>
</Link>
<p className="pl-3 pr-3 text-lg text-mineshaft-400/70">/</p>
{breadcrumbs ? (
// scott: remove /admin as we show server console above
<BreadcrumbContainer breadcrumbs={breadcrumbs.slice(1) as TBreadcrumbFormat[]} />
) : null}
</>

View File

@@ -89,11 +89,7 @@ const Content = ({ onClose }: ContentProps) => {
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
render={({ field, fieldState: { error } }) => (
<FormControl
isError={Boolean(error)}
errorText={error?.message}
label="Assign Organization Admins"
>
<FormControl isError={Boolean(error)} errorText={error?.message} label="User">
<FilterableSelect
isLoading={searchUserFilter !== debouncedSearchTerm || isFetching}
className="w-full"
@@ -120,7 +116,7 @@ const Content = ({ onClose }: ContentProps) => {
isDisabled={isSubmitting}
colorSchema="secondary"
>
Grant Server Admin
Grant
</Button>
<Button onClick={() => onClose()} variant="plain" colorSchema="secondary">
Cancel
@@ -133,7 +129,11 @@ const Content = ({ onClose }: ContentProps) => {
export const AddServerAdminModal = ({ isOpen, onOpenChange }: Props) => {
return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent bodyClassName="overflow-visible" title="Grant Server Admin">
<ModalContent
bodyClassName="overflow-visible"
title="Grant Server Admin"
subTitle="Grant server admin status to a user"
>
<Content onClose={() => onOpenChange(false)} />
</ModalContent>
</Modal>

View File

@@ -424,14 +424,14 @@ export const ServerAdminsTable = () => {
/>
<DeleteActionModal
isOpen={popUp.removeUsers.isOpen}
title="Are you sure you want to remove the following users?"
title="Are you sure you want to delete the following users?"
onChange={(isOpen) => handlePopUpToggle("removeUsers", isOpen)}
deleteKey="confirm"
onDeleteApproved={() => handleRemoveUsers()}
buttonText="Remove"
>
<div className="mt-4 text-sm text-mineshaft-400">
The following members will be removed:
The following users will be deleted:
</div>
<div className="mt-2 max-h-[20rem] overflow-y-auto rounded border border-mineshaft-600 bg-red/10 p-4 pl-8 text-sm text-red-200">
<ul className="list-disc">
@@ -458,7 +458,7 @@ export const ServerAdminsTable = () => {
className="ml-1 mt-[0.05rem] inline-flex w-min items-center gap-1.5 whitespace-nowrap"
>
<FontAwesomeIcon icon={faWarning} />
<span>Removing Yourself</span>
<span>Deleting Yourself</span>
</Badge>
</div>
</Tooltip>

View File

@@ -5,6 +5,7 @@ import {
faBuilding,
faCircleQuestion,
faEllipsisV,
faEnvelope,
faEye,
faMagnifyingGlass,
faPlus,
@@ -52,6 +53,7 @@ import {
} from "@app/hooks/api";
import { OrganizationWithProjects } from "@app/hooks/api/admin/types";
import { OrderByDirection } from "@app/hooks/api/generic/types";
import { OrgMembershipStatus } from "@app/hooks/api/organization/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
import { AddOrganizationModal } from "@app/pages/admin/ResourceOverviewPage/components/AddOrganizationModal";
@@ -120,7 +122,7 @@ const ViewMembersModalContent = ({
switch (orderBy) {
case MembersOrderBy.Email:
valueOne = memberOne.user.email || memberOne.user.username;
valueTwo = memberTwo.user.email || memberOne.user.username;
valueTwo = memberTwo.user.email || memberTwo.user.username;
break;
case MembersOrderBy.Name:
default:
@@ -255,23 +257,26 @@ const ViewMembersModalContent = ({
<Td className="max-w-0">
<div className="flex items-center">
<p className="truncate">{username || email}</p>
{role === OrgMembershipRole.Admin && status !== "accepted" && (
<Button
isDisabled={resendOrgInvite.isPending}
className="ml-2 h-8 border-mineshaft-600 bg-mineshaft-800/50 font-normal"
colorSchema="primary"
variant="outline_bg"
isLoading={
resendOrgInvite.isPending && resendInviteId === member.membershipId
}
onClick={(e) => {
onResendInvite(member.membershipId);
e.stopPropagation();
}}
>
Resend Invite
</Button>
)}
{role === OrgMembershipRole.Admin &&
status !== OrgMembershipStatus.Accepted && (
<Button
isDisabled={resendOrgInvite.isPending}
className="ml-2 h-7 border-mineshaft-600 bg-mineshaft-800/50 font-normal"
colorSchema="primary"
variant="outline_bg"
size="xs"
isLoading={
resendOrgInvite.isPending && resendInviteId === member.membershipId
}
leftIcon={<FontAwesomeIcon icon={faEnvelope} />}
onClick={(e) => {
onResendInvite(member.membershipId);
e.stopPropagation();
}}
>
Resend Invite
</Button>
)}
</div>
</Td>
<Td>
@@ -485,9 +490,9 @@ const OrganizationsPanelTable = ({
{!org.members.some(
(member) =>
member.role === OrgMembershipRole.Admin &&
member.status === "accepted"
member.status === OrgMembershipStatus.Accepted
) && (
<Tooltip content="No admins have accepted their invites.">
<Tooltip content="No admins have accepted their invitations.">
<div className="ml-1">
<Badge>
<FontAwesomeIcon icon={faWarning} />

View File

@@ -521,14 +521,14 @@ export const UserIdentitiesTable = () => {
/>
<DeleteActionModal
isOpen={popUp.removeUsers.isOpen}
title="Are you sure you want to remove the following users?"
title="Are you sure you want to delete the following users?"
onChange={(isOpen) => handlePopUpToggle("removeUsers", isOpen)}
deleteKey="confirm"
onDeleteApproved={() => handleRemoveUsers()}
buttonText="Remove"
buttonText="Delete"
>
<div className="mt-4 text-sm text-mineshaft-400">
The following members will be removed:
The following users will be deleted:
</div>
<div className="mt-2 max-h-[20rem] overflow-y-auto rounded border border-mineshaft-600 bg-red/10 p-4 pl-8 text-sm text-red-200">
<ul className="list-disc">
@@ -555,7 +555,7 @@ export const UserIdentitiesTable = () => {
className="ml-1 mt-[0.05rem] inline-flex w-min items-center gap-1.5 whitespace-nowrap"
>
<FontAwesomeIcon icon={faWarning} />
<span>Removing Yourself</span>
<span>Deleting Yourself</span>
</Badge>
</div>
</Tooltip>