From ce870ff577064ef63513e379dd03450045ca6a4f Mon Sep 17 00:00:00 2001 From: Weves Date: Sat, 16 Dec 2023 17:22:38 -0800 Subject: [PATCH] Re-style user group pages --- backend/ee/danswer/user_groups/sync.py | 3 - .../app/ee/admin/groups/ConnectorEditor.tsx | 4 +- web/src/app/ee/admin/groups/UserEditor.tsx | 6 +- .../ee/admin/groups/UserGroupCreationForm.tsx | 225 +++++++------- .../app/ee/admin/groups/UserGroupsTable.tsx | 138 ++++++++- .../groups/[groupId]/AddConnectorForm.tsx | 6 +- .../admin/groups/[groupId]/GroupDisplay.tsx | 278 ++++++++++-------- .../app/ee/admin/groups/[groupId]/page.tsx | 21 +- web/src/app/ee/admin/groups/page.tsx | 30 +- .../admin/performance/query-history/page.tsx | 1 - web/src/components/DeleteButton.tsx | 2 +- 11 files changed, 442 insertions(+), 272 deletions(-) diff --git a/backend/ee/danswer/user_groups/sync.py b/backend/ee/danswer/user_groups/sync.py index 72f198d58..24c8513c7 100644 --- a/backend/ee/danswer/user_groups/sync.py +++ b/backend/ee/danswer/user_groups/sync.py @@ -1,8 +1,5 @@ from sqlalchemy.orm import Session -from danswer.document_index.factory import get_default_document_index -from danswer.document_index.interfaces import DocumentIndex -from danswer.document_index.interfaces import UpdateRequest from danswer.access.access import get_access_for_documents from danswer.db.document import prepare_to_modify_documents from danswer.db.engine import get_sqlalchemy_engine diff --git a/web/src/app/ee/admin/groups/ConnectorEditor.tsx b/web/src/app/ee/admin/groups/ConnectorEditor.tsx index fa06fa418..d632e5f6a 100644 --- a/web/src/app/ee/admin/groups/ConnectorEditor.tsx +++ b/web/src/app/ee/admin/groups/ConnectorEditor.tsx @@ -29,11 +29,11 @@ export const ConnectorEditor = ({ py-1 rounded-lg border - border-gray-700 + border-border w-fit flex cursor-pointer ` + - (isSelected ? " bg-gray-600" : " hover:bg-gray-700") + (isSelected ? " bg-hover" : " hover:bg-hover-light") } onClick={() => { if (isSelected) { diff --git a/web/src/app/ee/admin/groups/UserEditor.tsx b/web/src/app/ee/admin/groups/UserEditor.tsx index 12112b7b2..b4e7354fd 100644 --- a/web/src/app/ee/admin/groups/UserEditor.tsx +++ b/web/src/app/ee/admin/groups/UserEditor.tsx @@ -42,8 +42,8 @@ export const UserEditor = ({ px-2 py-1 border - border-gray-700 - hover:bg-gray-900 + border-border + hover:bg-hover-light cursor-pointer`} > {selectedUser.email} @@ -73,7 +73,7 @@ export const UserEditor = ({ ]); }} itemComponent={({ option }) => ( -
+
{option.name}
diff --git a/web/src/app/ee/admin/groups/UserGroupCreationForm.tsx b/web/src/app/ee/admin/groups/UserGroupCreationForm.tsx index d4a5d4873..edbc83027 100644 --- a/web/src/app/ee/admin/groups/UserGroupCreationForm.tsx +++ b/web/src/app/ee/admin/groups/UserGroupCreationForm.tsx @@ -1,13 +1,16 @@ -import { ArrayHelpers, FieldArray, Form, Formik } from "formik"; +import { Form, Formik } from "formik"; import * as Yup from "yup"; import { PopupSpec } from "@/components/admin/connectors/Popup"; -import { ConnectorIndexingStatus, DocumentSet, User } from "@/lib/types"; +import { ConnectorIndexingStatus, User } from "@/lib/types"; import { TextFormField } from "@/components/admin/connectors/Field"; import { ConnectorTitle } from "@/components/admin/connectors/ConnectorTitle"; import { createUserGroup } from "./lib"; import { UserGroup } from "./types"; import { UserEditor } from "./UserEditor"; import { ConnectorEditor } from "./ConnectorEditor"; +import { Modal } from "@/components/Modal"; +import { XIcon } from "@/components/icons/icons"; +import { Button, Divider } from "@tremor/react"; interface UserGroupCreationFormProps { onClose: () => void; @@ -27,119 +30,123 @@ export const UserGroupCreationForm = ({ const isUpdate = existingUserGroup !== undefined; return ( -
-
-
event.stopPropagation()} - > - { - formikHelpers.setSubmitting(true); - let response; - response = await createUserGroup(values); - formikHelpers.setSubmitting(false); - if (response.ok) { - setPopup({ - message: isUpdate - ? "Successfully updated user group!" - : "Successfully created user group!", - type: "success", - }); - onClose(); - } else { - const responseJson = await response.json(); - const errorMsg = responseJson.detail || responseJson.message; - setPopup({ - message: isUpdate - ? `Error updating user group - ${errorMsg}` - : `Error creating user group - ${errorMsg}`, - type: "error", - }); - } - }} + +
+

+ {isUpdate ? "Update a User Group" : "Create a new User Group"} +
- {({ isSubmitting, values, setFieldValue }) => ( -
-

- {isUpdate ? "Update a User Group" : "Create a new User Group"} + +

+

+ + + + { + formikHelpers.setSubmitting(true); + let response; + response = await createUserGroup(values); + formikHelpers.setSubmitting(false); + if (response.ok) { + setPopup({ + message: isUpdate + ? "Successfully updated user group!" + : "Successfully created user group!", + type: "success", + }); + onClose(); + } else { + const responseJson = await response.json(); + const errorMsg = responseJson.detail || responseJson.message; + setPopup({ + message: isUpdate + ? `Error updating user group - ${errorMsg}` + : `Error creating user group - ${errorMsg}`, + type: "error", + }); + } + }} + > + {({ isSubmitting, values, setFieldValue }) => ( + +
+ + + + +

+ Select which connectors this group has access to:

-
- -
-

- Select which connectors this group has access to: -

-

- All documents indexed by the selected connectors will be - visible to users in this group. -

+

+ All documents indexed by the selected connectors will be + visible to users in this group. +

- - setFieldValue("cc_pair_ids", ccPairsIds) + + setFieldValue("cc_pair_ids", ccPairsIds) + } + /> + + + +

+ Select which Users should be a part of this Group. +

+

+ All selected users will be able to search through all + documents indexed by the selected connectors. +

+
+ + setFieldValue("user_ids", userIds) } + allUsers={users} + existingUsers={[]} /> - -
- -

- Select which Users should be a part of this Group. -

-

- All selected users will be able to search through all - documents indexed by the selected connectors. -

-
- - setFieldValue("user_ids", userIds) - } - allUsers={users} - existingUsers={[]} - /> -
-
- -
- - )} - -
+
+ +
+
+ + )} +
-
+ ); }; diff --git a/web/src/app/ee/admin/groups/UserGroupsTable.tsx b/web/src/app/ee/admin/groups/UserGroupsTable.tsx index 27703ddfa..3666b3c52 100644 --- a/web/src/app/ee/admin/groups/UserGroupsTable.tsx +++ b/web/src/app/ee/admin/groups/UserGroupsTable.tsx @@ -1,5 +1,13 @@ "use client"; +import { + Table, + TableHead, + TableRow, + TableHeaderCell, + TableBody, + TableCell, +} from "@tremor/react"; import { UserGroup } from "./types"; import { PopupSpec } from "@/components/admin/connectors/Popup"; import { LoadingAnimation } from "@/components/Loading"; @@ -8,14 +16,16 @@ import { ConnectorTitle } from "@/components/admin/connectors/ConnectorTitle"; import { TrashIcon } from "@/components/icons/icons"; import { deleteUserGroup } from "./lib"; import { useRouter } from "next/navigation"; -import { FiUser } from "react-icons/fi"; +import { FiEdit, FiUser } from "react-icons/fi"; import { User } from "@/lib/types"; +import Link from "next/link"; +import { DeleteButton } from "@/components/DeleteButton"; const MAX_USERS_TO_DISPLAY = 6; const SimpleUserDisplay = ({ user }: { user: User }) => { return ( -
+
{user.email}
); @@ -45,6 +55,130 @@ export const UserGroupsTable = ({ } }); + return ( +
+ + + + Name + Connectors + Users + Status + Delete + + + + {userGroups + .filter((userGroup) => !userGroup.is_up_for_deletion) + .map((userGroup) => { + return ( + + + + +

{userGroup.name}

+ +
+ + {userGroup.cc_pairs.length > 0 ? ( +
+ {userGroup.cc_pairs.map((ccPairDescriptor, ind) => { + return ( +
+ +
+ ); + })} +
+ ) : ( + "-" + )} +
+ + {userGroup.users.length > 0 ? ( +
+ {userGroup.users.length <= MAX_USERS_TO_DISPLAY ? ( + userGroup.users.map((user) => { + return ( + + ); + }) + ) : ( +
+ {userGroup.users + .slice(0, MAX_USERS_TO_DISPLAY) + .map((user) => { + return ( + + ); + })} +
+ + {userGroup.users.length - MAX_USERS_TO_DISPLAY}{" "} + more +
+
+ )} +
+ ) : ( + "-" + )} +
+ + {userGroup.is_up_to_date ? ( +
Up to date!
+ ) : ( +
+ +
+ )} +
+ + { + event.stopPropagation(); + const response = await deleteUserGroup(userGroup.id); + if (response.ok) { + setPopup({ + message: `User Group "${userGroup.name}" deleted`, + type: "success", + }); + } else { + const errorMsg = (await response.json()).detail; + setPopup({ + message: `Failed to delete User Group - ${errorMsg}`, + type: "error", + }); + } + refresh(); + }} + /> + +
+ ); + })} +
+
+
+ ); + return (
= ({ py-1 my-1 border - border-gray-700 - hover:bg-gray-900 + border-border + hover:bg-hover cursor-pointer`} > = ({ ]); }} itemComponent={({ option }) => ( -
+
- Status:{" "} + Status:{" "} {userGroup.is_up_to_date ? ( -
Up to date
+
Up to date
) : ( -
+
)}
+ +

Users

{userGroup.users.length > 0 ? ( - { - return { - email:
{user.email}
, - remove: ( -
-
{ - const response = await updateUserGroup(userGroup.id, { - user_ids: userGroup.users - .filter( - (userGroupUser) => userGroupUser.id !== user.id - ) - .map((userGroupUser) => userGroupUser.id), - cc_pair_ids: userGroup.cc_pairs.map( - (ccPair) => ccPair.id - ), - }); - if (response.ok) { - setPopup({ - message: "Successfully removed user from group", - type: "success", - }); - } else { - const responseJson = await response.json(); - const errorMsg = - responseJson.detail || responseJson.message; - setPopup({ - message: `Error removing user from group - ${errorMsg}`, - type: "error", - }); - } - refreshUserGroup(); - }} - > - -
-
- ), - }; - })} - /> + <> + + + + Email + +
Remove User
+
+
+
+ + {userGroup.users.map((user) => { + return ( + + + {user.email} + + +
+
+ { + const response = await updateUserGroup( + userGroup.id, + { + user_ids: userGroup.users + .filter( + (userGroupUser) => + userGroupUser.id !== user.id + ) + .map((userGroupUser) => userGroupUser.id), + cc_pair_ids: userGroup.cc_pairs.map( + (ccPair) => ccPair.id + ), + } + ); + if (response.ok) { + setPopup({ + message: + "Successfully removed user from group", + type: "success", + }); + } else { + const responseJson = await response.json(); + const errorMsg = + responseJson.detail || responseJson.message; + setPopup({ + message: `Error removing user from group - ${errorMsg}`, + type: "error", + }); + } + refreshUserGroup(); + }} + /> +
+
+
+
+ ); + })} +
+
+ ) : (
No users in this group...
)} @@ -112,6 +132,8 @@ export const GroupDisplay = ({ +
- + {data.length > 0 && ( +
+ + +
+ )} {showForm && ( { @@ -73,10 +81,10 @@ const Main = () => { const Page = () => { return (
-
- -

Manage Users Groups

-
+ } + />
diff --git a/web/src/app/ee/admin/performance/query-history/page.tsx b/web/src/app/ee/admin/performance/query-history/page.tsx index e9426810c..03247ec96 100644 --- a/web/src/app/ee/admin/performance/query-history/page.tsx +++ b/web/src/app/ee/admin/performance/query-history/page.tsx @@ -7,7 +7,6 @@ import { DatabaseIcon } from "@/components/icons/icons"; export default function QueryHistoryPage() { return (
- } /> diff --git a/web/src/components/DeleteButton.tsx b/web/src/components/DeleteButton.tsx index 0a381af01..498161c52 100644 --- a/web/src/components/DeleteButton.tsx +++ b/web/src/components/DeleteButton.tsx @@ -4,7 +4,7 @@ export function DeleteButton({ onClick, disabled, }: { - onClick?: () => void; + onClick?: (event: React.MouseEvent) => void | Promise; disabled?: boolean; }) { return (