mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-06-28 17:01:10 +02:00
Re-style user group pages
This commit is contained in:
parent
a52711967f
commit
ce870ff577
@ -1,8 +1,5 @@
|
|||||||
from sqlalchemy.orm import Session
|
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.access.access import get_access_for_documents
|
||||||
from danswer.db.document import prepare_to_modify_documents
|
from danswer.db.document import prepare_to_modify_documents
|
||||||
from danswer.db.engine import get_sqlalchemy_engine
|
from danswer.db.engine import get_sqlalchemy_engine
|
||||||
|
@ -29,11 +29,11 @@ export const ConnectorEditor = ({
|
|||||||
py-1
|
py-1
|
||||||
rounded-lg
|
rounded-lg
|
||||||
border
|
border
|
||||||
border-gray-700
|
border-border
|
||||||
w-fit
|
w-fit
|
||||||
flex
|
flex
|
||||||
cursor-pointer ` +
|
cursor-pointer ` +
|
||||||
(isSelected ? " bg-gray-600" : " hover:bg-gray-700")
|
(isSelected ? " bg-hover" : " hover:bg-hover-light")
|
||||||
}
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
|
@ -42,8 +42,8 @@ export const UserEditor = ({
|
|||||||
px-2
|
px-2
|
||||||
py-1
|
py-1
|
||||||
border
|
border
|
||||||
border-gray-700
|
border-border
|
||||||
hover:bg-gray-900
|
hover:bg-hover-light
|
||||||
cursor-pointer`}
|
cursor-pointer`}
|
||||||
>
|
>
|
||||||
{selectedUser.email} <FiX className="ml-1 my-auto" />
|
{selectedUser.email} <FiX className="ml-1 my-auto" />
|
||||||
@ -73,7 +73,7 @@ export const UserEditor = ({
|
|||||||
]);
|
]);
|
||||||
}}
|
}}
|
||||||
itemComponent={({ option }) => (
|
itemComponent={({ option }) => (
|
||||||
<div className="flex px-4 py-2.5 hover:bg-gray-800 cursor-pointer">
|
<div className="flex px-4 py-2.5 cursor-pointer hover:bg-hover">
|
||||||
<UsersIcon className="mr-2 my-auto" />
|
<UsersIcon className="mr-2 my-auto" />
|
||||||
{option.name}
|
{option.name}
|
||||||
<div className="ml-auto my-auto">
|
<div className="ml-auto my-auto">
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import { ArrayHelpers, FieldArray, Form, Formik } from "formik";
|
import { Form, Formik } from "formik";
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import { PopupSpec } from "@/components/admin/connectors/Popup";
|
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 { TextFormField } from "@/components/admin/connectors/Field";
|
||||||
import { ConnectorTitle } from "@/components/admin/connectors/ConnectorTitle";
|
import { ConnectorTitle } from "@/components/admin/connectors/ConnectorTitle";
|
||||||
import { createUserGroup } from "./lib";
|
import { createUserGroup } from "./lib";
|
||||||
import { UserGroup } from "./types";
|
import { UserGroup } from "./types";
|
||||||
import { UserEditor } from "./UserEditor";
|
import { UserEditor } from "./UserEditor";
|
||||||
import { ConnectorEditor } from "./ConnectorEditor";
|
import { ConnectorEditor } from "./ConnectorEditor";
|
||||||
|
import { Modal } from "@/components/Modal";
|
||||||
|
import { XIcon } from "@/components/icons/icons";
|
||||||
|
import { Button, Divider } from "@tremor/react";
|
||||||
|
|
||||||
interface UserGroupCreationFormProps {
|
interface UserGroupCreationFormProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@ -27,15 +30,23 @@ export const UserGroupCreationForm = ({
|
|||||||
const isUpdate = existingUserGroup !== undefined;
|
const isUpdate = existingUserGroup !== undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Modal onOutsideClick={onClose}>
|
||||||
|
<div className="px-8 py-6 bg-background">
|
||||||
|
<h2 className="text-xl font-bold flex">
|
||||||
|
{isUpdate ? "Update a User Group" : "Create a new User Group"}
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-30"
|
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
|
className="ml-auto hover:bg-hover p-1.5 rounded"
|
||||||
>
|
>
|
||||||
<div
|
<XIcon
|
||||||
className="bg-gray-800 rounded-lg border border-gray-700 shadow-lg relative w-1/2 text-sm"
|
size={20}
|
||||||
onClick={(event) => event.stopPropagation()}
|
className="my-auto flex flex-shrink-0 cursor-pointer"
|
||||||
>
|
/>
|
||||||
|
</div>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
name: existingUserGroup ? existingUserGroup.name : "",
|
name: existingUserGroup ? existingUserGroup.name : "",
|
||||||
@ -74,9 +85,6 @@ export const UserGroupCreationForm = ({
|
|||||||
>
|
>
|
||||||
{({ isSubmitting, values, setFieldValue }) => (
|
{({ isSubmitting, values, setFieldValue }) => (
|
||||||
<Form>
|
<Form>
|
||||||
<h2 className="text-xl font-bold mb-3 border-b border-gray-600 pt-4 pb-3 bg-gray-700 px-6">
|
|
||||||
{isUpdate ? "Update a User Group" : "Create a new User Group"}
|
|
||||||
</h2>
|
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<TextFormField
|
<TextFormField
|
||||||
name="name"
|
name="name"
|
||||||
@ -85,7 +93,9 @@ export const UserGroupCreationForm = ({
|
|||||||
disabled={isUpdate}
|
disabled={isUpdate}
|
||||||
autoCompleteDisabled={true}
|
autoCompleteDisabled={true}
|
||||||
/>
|
/>
|
||||||
<div className="border-t border-gray-600 py-2" />
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
<h2 className="mb-1 font-medium">
|
<h2 className="mb-1 font-medium">
|
||||||
Select which connectors this group has access to:
|
Select which connectors this group has access to:
|
||||||
</h2>
|
</h2>
|
||||||
@ -102,7 +112,7 @@ export const UserGroupCreationForm = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="border-t border-gray-600 py-2" />
|
<Divider />
|
||||||
|
|
||||||
<h2 className="mb-1 font-medium">
|
<h2 className="mb-1 font-medium">
|
||||||
Select which Users should be a part of this Group.
|
Select which Users should be a part of this Group.
|
||||||
@ -122,24 +132,21 @@ export const UserGroupCreationForm = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
size="xs"
|
||||||
|
color="green"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
className={
|
className="mx-auto w-64"
|
||||||
"bg-slate-500 hover:bg-slate-700 text-white " +
|
|
||||||
"font-bold py-2 px-4 rounded focus:outline-none " +
|
|
||||||
"focus:shadow-outline w-full max-w-sm mx-auto"
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{isUpdate ? "Update!" : "Create!"}
|
{isUpdate ? "Update!" : "Create!"}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Modal>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
TableHeaderCell,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
} from "@tremor/react";
|
||||||
import { UserGroup } from "./types";
|
import { UserGroup } from "./types";
|
||||||
import { PopupSpec } from "@/components/admin/connectors/Popup";
|
import { PopupSpec } from "@/components/admin/connectors/Popup";
|
||||||
import { LoadingAnimation } from "@/components/Loading";
|
import { LoadingAnimation } from "@/components/Loading";
|
||||||
@ -8,14 +16,16 @@ import { ConnectorTitle } from "@/components/admin/connectors/ConnectorTitle";
|
|||||||
import { TrashIcon } from "@/components/icons/icons";
|
import { TrashIcon } from "@/components/icons/icons";
|
||||||
import { deleteUserGroup } from "./lib";
|
import { deleteUserGroup } from "./lib";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { FiUser } from "react-icons/fi";
|
import { FiEdit, FiUser } from "react-icons/fi";
|
||||||
import { User } from "@/lib/types";
|
import { User } from "@/lib/types";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { DeleteButton } from "@/components/DeleteButton";
|
||||||
|
|
||||||
const MAX_USERS_TO_DISPLAY = 6;
|
const MAX_USERS_TO_DISPLAY = 6;
|
||||||
|
|
||||||
const SimpleUserDisplay = ({ user }: { user: User }) => {
|
const SimpleUserDisplay = ({ user }: { user: User }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex my-0.5 text-gray-200">
|
<div className="flex my-0.5">
|
||||||
<FiUser className="mr-2 my-auto" /> {user.email}
|
<FiUser className="mr-2 my-auto" /> {user.email}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -45,6 +55,130 @@ export const UserGroupsTable = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Table className="overflow-visible">
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableHeaderCell>Name</TableHeaderCell>
|
||||||
|
<TableHeaderCell>Connectors</TableHeaderCell>
|
||||||
|
<TableHeaderCell>Users</TableHeaderCell>
|
||||||
|
<TableHeaderCell>Status</TableHeaderCell>
|
||||||
|
<TableHeaderCell>Delete</TableHeaderCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{userGroups
|
||||||
|
.filter((userGroup) => !userGroup.is_up_for_deletion)
|
||||||
|
.map((userGroup) => {
|
||||||
|
return (
|
||||||
|
<TableRow key={userGroup.id}>
|
||||||
|
<TableCell>
|
||||||
|
<Link
|
||||||
|
className="whitespace-normal break-all flex cursor-pointer p-2 rounded hover:bg-hover w-fit"
|
||||||
|
href={`/admin/groups/${userGroup.id}`}
|
||||||
|
>
|
||||||
|
<FiEdit className="my-auto mr-2" />
|
||||||
|
<p className="text font-medium">{userGroup.name}</p>
|
||||||
|
</Link>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{userGroup.cc_pairs.length > 0 ? (
|
||||||
|
<div>
|
||||||
|
{userGroup.cc_pairs.map((ccPairDescriptor, ind) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
ind !== userGroup.cc_pairs.length - 1
|
||||||
|
? "mb-3"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
key={ccPairDescriptor.id}
|
||||||
|
>
|
||||||
|
<ConnectorTitle
|
||||||
|
connector={ccPairDescriptor.connector}
|
||||||
|
ccPairId={ccPairDescriptor.id}
|
||||||
|
ccPairName={ccPairDescriptor.name}
|
||||||
|
showMetadata={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
"-"
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{userGroup.users.length > 0 ? (
|
||||||
|
<div>
|
||||||
|
{userGroup.users.length <= MAX_USERS_TO_DISPLAY ? (
|
||||||
|
userGroup.users.map((user) => {
|
||||||
|
return (
|
||||||
|
<SimpleUserDisplay key={user.id} user={user} />
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
{userGroup.users
|
||||||
|
.slice(0, MAX_USERS_TO_DISPLAY)
|
||||||
|
.map((user) => {
|
||||||
|
return (
|
||||||
|
<SimpleUserDisplay
|
||||||
|
key={user.id}
|
||||||
|
user={user}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<div>
|
||||||
|
+ {userGroup.users.length - MAX_USERS_TO_DISPLAY}{" "}
|
||||||
|
more
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
"-"
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{userGroup.is_up_to_date ? (
|
||||||
|
<div className="text-success">Up to date!</div>
|
||||||
|
) : (
|
||||||
|
<div className="w-10">
|
||||||
|
<LoadingAnimation text="Syncing" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<DeleteButton
|
||||||
|
onClick={async (event) => {
|
||||||
|
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();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BasicTable
|
<BasicTable
|
||||||
|
@ -50,8 +50,8 @@ export const AddConnectorForm: React.FC<AddConnectorFormProps> = ({
|
|||||||
py-1
|
py-1
|
||||||
my-1
|
my-1
|
||||||
border
|
border
|
||||||
border-gray-700
|
border-border
|
||||||
hover:bg-gray-900
|
hover:bg-hover
|
||||||
cursor-pointer`}
|
cursor-pointer`}
|
||||||
>
|
>
|
||||||
<ConnectorTitle
|
<ConnectorTitle
|
||||||
@ -99,7 +99,7 @@ export const AddConnectorForm: React.FC<AddConnectorFormProps> = ({
|
|||||||
]);
|
]);
|
||||||
}}
|
}}
|
||||||
itemComponent={({ option }) => (
|
itemComponent={({ option }) => (
|
||||||
<div className="flex px-4 py-2.5 hover:bg-gray-800 cursor-pointer">
|
<div className="flex px-4 py-2.5 hover:bg-hover cursor-pointer">
|
||||||
<div className="my-auto">
|
<div className="my-auto">
|
||||||
<ConnectorTitle
|
<ConnectorTitle
|
||||||
ccPairId={option?.metadata?.ccPairId as number}
|
ccPairId={option?.metadata?.ccPairId as number}
|
||||||
|
@ -3,15 +3,24 @@
|
|||||||
import { usePopup } from "@/components/admin/connectors/Popup";
|
import { usePopup } from "@/components/admin/connectors/Popup";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { UserGroup } from "../types";
|
import { UserGroup } from "../types";
|
||||||
import { BasicTable } from "@/components/admin/connectors/BasicTable";
|
|
||||||
import { ConnectorTitle } from "@/components/admin/connectors/ConnectorTitle";
|
import { ConnectorTitle } from "@/components/admin/connectors/ConnectorTitle";
|
||||||
import { Button } from "@/components/Button";
|
|
||||||
import { AddMemberForm } from "./AddMemberForm";
|
import { AddMemberForm } from "./AddMemberForm";
|
||||||
import { TrashIcon } from "@/components/icons/icons";
|
|
||||||
import { updateUserGroup } from "./lib";
|
import { updateUserGroup } from "./lib";
|
||||||
import { LoadingAnimation } from "@/components/Loading";
|
import { LoadingAnimation } from "@/components/Loading";
|
||||||
import { ConnectorIndexingStatus, User } from "@/lib/types";
|
import { ConnectorIndexingStatus, User } from "@/lib/types";
|
||||||
import { AddConnectorForm } from "./AddConnectorForm";
|
import { AddConnectorForm } from "./AddConnectorForm";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
TableHeaderCell,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
Divider,
|
||||||
|
Button,
|
||||||
|
Text,
|
||||||
|
} from "@tremor/react";
|
||||||
|
import { DeleteButton } from "@/components/DeleteButton";
|
||||||
|
|
||||||
interface GroupDisplayProps {
|
interface GroupDisplayProps {
|
||||||
users: User[];
|
users: User[];
|
||||||
@ -35,55 +44,64 @@ export const GroupDisplay = ({
|
|||||||
{popup}
|
{popup}
|
||||||
|
|
||||||
<div className="text-sm mb-3 flex">
|
<div className="text-sm mb-3 flex">
|
||||||
<b className="mr-1">Status:</b>{" "}
|
<Text className="mr-1">Status:</Text>{" "}
|
||||||
{userGroup.is_up_to_date ? (
|
{userGroup.is_up_to_date ? (
|
||||||
<div className="text-emerald-600">Up to date</div>
|
<div className="text-success font-bold">Up to date</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-gray-300">
|
<div className="text-accent font-bold">
|
||||||
<LoadingAnimation text="Syncing" />
|
<LoadingAnimation text="Syncing" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
<div className="flex w-full">
|
<div className="flex w-full">
|
||||||
<h2 className="text-xl font-bold">Users</h2>
|
<h2 className="text-xl font-bold">Users</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
{userGroup.users.length > 0 ? (
|
{userGroup.users.length > 0 ? (
|
||||||
<BasicTable
|
<>
|
||||||
columns={[
|
<Table className="overflow-visible">
|
||||||
{
|
<TableHead>
|
||||||
header: "Email",
|
<TableRow>
|
||||||
key: "email",
|
<TableHeaderCell>Email</TableHeaderCell>
|
||||||
},
|
<TableHeaderCell className="flex w-full">
|
||||||
{
|
<div className="ml-auto">Remove User</div>
|
||||||
header: "Remove User",
|
</TableHeaderCell>
|
||||||
key: "remove",
|
</TableRow>
|
||||||
alignment: "right",
|
</TableHead>
|
||||||
},
|
<TableBody>
|
||||||
]}
|
{userGroup.users.map((user) => {
|
||||||
data={userGroup.users.map((user) => {
|
return (
|
||||||
return {
|
<TableRow key={user.id}>
|
||||||
email: <div>{user.email}</div>,
|
<TableCell className="whitespace-normal break-all">
|
||||||
remove: (
|
{user.email}
|
||||||
<div className="flex">
|
</TableCell>
|
||||||
<div
|
<TableCell>
|
||||||
className="cursor-pointer ml-auto mr-1"
|
<div className="flex w-full">
|
||||||
|
<div className="ml-auto m-2">
|
||||||
|
<DeleteButton
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const response = await updateUserGroup(userGroup.id, {
|
const response = await updateUserGroup(
|
||||||
|
userGroup.id,
|
||||||
|
{
|
||||||
user_ids: userGroup.users
|
user_ids: userGroup.users
|
||||||
.filter(
|
.filter(
|
||||||
(userGroupUser) => userGroupUser.id !== user.id
|
(userGroupUser) =>
|
||||||
|
userGroupUser.id !== user.id
|
||||||
)
|
)
|
||||||
.map((userGroupUser) => userGroupUser.id),
|
.map((userGroupUser) => userGroupUser.id),
|
||||||
cc_pair_ids: userGroup.cc_pairs.map(
|
cc_pair_ids: userGroup.cc_pairs.map(
|
||||||
(ccPair) => ccPair.id
|
(ccPair) => ccPair.id
|
||||||
),
|
),
|
||||||
});
|
}
|
||||||
|
);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
setPopup({
|
setPopup({
|
||||||
message: "Successfully removed user from group",
|
message:
|
||||||
|
"Successfully removed user from group",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -97,14 +115,16 @@ export const GroupDisplay = ({
|
|||||||
}
|
}
|
||||||
refreshUserGroup();
|
refreshUserGroup();
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
<TrashIcon />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-sm">No users in this group...</div>
|
<div className="text-sm">No users in this group...</div>
|
||||||
)}
|
)}
|
||||||
@ -112,6 +132,8 @@ export const GroupDisplay = ({
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="mt-3"
|
className="mt-3"
|
||||||
|
size="xs"
|
||||||
|
color="green"
|
||||||
onClick={() => setAddMemberFormVisible(true)}
|
onClick={() => setAddMemberFormVisible(true)}
|
||||||
disabled={!userGroup.is_up_to_date}
|
disabled={!userGroup.is_up_to_date}
|
||||||
>
|
>
|
||||||
@ -130,37 +152,40 @@ export const GroupDisplay = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<h2 className="text-xl font-bold mt-4">Connectors</h2>
|
<Divider />
|
||||||
|
|
||||||
|
<h2 className="text-xl font-bold mt-8">Connectors</h2>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
{userGroup.cc_pairs.length > 0 ? (
|
{userGroup.cc_pairs.length > 0 ? (
|
||||||
<BasicTable
|
<>
|
||||||
columns={[
|
<Table className="overflow-visible">
|
||||||
{
|
<TableHead>
|
||||||
header: "Connector",
|
<TableRow>
|
||||||
key: "connector_name",
|
<TableHeaderCell>Connector</TableHeaderCell>
|
||||||
},
|
<TableHeaderCell className="flex w-full">
|
||||||
{
|
<div className="ml-auto">Remove Connector</div>
|
||||||
header: "Remove Connector",
|
</TableHeaderCell>
|
||||||
key: "delete",
|
</TableRow>
|
||||||
alignment: "right",
|
</TableHead>
|
||||||
},
|
<TableBody>
|
||||||
]}
|
{userGroup.cc_pairs.map((ccPair) => {
|
||||||
data={userGroup.cc_pairs.map((ccPair) => {
|
return (
|
||||||
return {
|
<TableRow key={ccPair.id}>
|
||||||
connector_name:
|
<TableCell className="whitespace-normal break-all">
|
||||||
(
|
|
||||||
<ConnectorTitle
|
<ConnectorTitle
|
||||||
connector={ccPair.connector}
|
connector={ccPair.connector}
|
||||||
ccPairId={ccPair.id}
|
ccPairId={ccPair.id}
|
||||||
ccPairName={ccPair.name}
|
ccPairName={ccPair.name}
|
||||||
/>
|
/>
|
||||||
) || "",
|
</TableCell>
|
||||||
delete: (
|
<TableCell>
|
||||||
<div className="flex">
|
<div className="flex w-full">
|
||||||
<div
|
<div className="ml-auto m-2">
|
||||||
className="cursor-pointer ml-auto mr-1"
|
<DeleteButton
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const response = await updateUserGroup(userGroup.id, {
|
const response = await updateUserGroup(
|
||||||
|
userGroup.id,
|
||||||
|
{
|
||||||
user_ids: userGroup.users.map(
|
user_ids: userGroup.users.map(
|
||||||
(userGroupUser) => userGroupUser.id
|
(userGroupUser) => userGroupUser.id
|
||||||
),
|
),
|
||||||
@ -170,7 +195,8 @@ export const GroupDisplay = ({
|
|||||||
userGroupCCPair.id != ccPair.id
|
userGroupCCPair.id != ccPair.id
|
||||||
)
|
)
|
||||||
.map((ccPair) => ccPair.id),
|
.map((ccPair) => ccPair.id),
|
||||||
});
|
}
|
||||||
|
);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
setPopup({
|
setPopup({
|
||||||
message:
|
message:
|
||||||
@ -188,14 +214,16 @@ export const GroupDisplay = ({
|
|||||||
}
|
}
|
||||||
refreshUserGroup();
|
refreshUserGroup();
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
<TrashIcon />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-sm">No connectors in this group...</div>
|
<div className="text-sm">No connectors in this group...</div>
|
||||||
)}
|
)}
|
||||||
@ -204,6 +232,8 @@ export const GroupDisplay = ({
|
|||||||
<Button
|
<Button
|
||||||
className="mt-3"
|
className="mt-3"
|
||||||
onClick={() => setAddConnectorFormVisible(true)}
|
onClick={() => setAddConnectorFormVisible(true)}
|
||||||
|
size="xs"
|
||||||
|
color="green"
|
||||||
disabled={!userGroup.is_up_to_date}
|
disabled={!userGroup.is_up_to_date}
|
||||||
>
|
>
|
||||||
Add Connectors
|
Add Connectors
|
||||||
|
@ -7,6 +7,8 @@ import { useSpecificUserGroup } from "./hook";
|
|||||||
import { ThreeDotsLoader } from "@/components/Loading";
|
import { ThreeDotsLoader } from "@/components/Loading";
|
||||||
import { useConnectorCredentialIndexingStatus, useUsers } from "@/lib/hooks";
|
import { useConnectorCredentialIndexingStatus, useUsers } from "@/lib/hooks";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import { BackButton } from "@/components/BackButton";
|
||||||
|
import { AdminPageTitle } from "@/components/admin/Title";
|
||||||
|
|
||||||
const Page = ({ params }: { params: { groupId: string } }) => {
|
const Page = ({ params }: { params: { groupId: string } }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -50,19 +52,12 @@ const Page = ({ params }: { params: { groupId: string } }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto container">
|
<div className="mx-auto container">
|
||||||
<div
|
<BackButton />
|
||||||
className="my-auto flex mb-1 hover:bg-gray-700 w-fit pr-2 cursor-pointer rounded-lg"
|
|
||||||
onClick={() => router.back()}
|
<AdminPageTitle
|
||||||
>
|
title={userGroup.name || "Unknown"}
|
||||||
<FiChevronLeft className="mr-1 my-auto" />
|
icon={<GroupsIcon size={32} />}
|
||||||
Back
|
/>
|
||||||
</div>
|
|
||||||
<div className="border-solid border-gray-600 border-b pb-2 mb-4 flex">
|
|
||||||
<GroupsIcon size={32} />
|
|
||||||
<h1 className="text-3xl font-bold pl-2">
|
|
||||||
{userGroup ? userGroup.name : <FiAlertCircle />}
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{userGroup ? (
|
{userGroup ? (
|
||||||
<GroupDisplay
|
<GroupDisplay
|
||||||
|
@ -5,10 +5,11 @@ import { UserGroupsTable } from "./UserGroupsTable";
|
|||||||
import { UserGroupCreationForm } from "./UserGroupCreationForm";
|
import { UserGroupCreationForm } from "./UserGroupCreationForm";
|
||||||
import { usePopup } from "@/components/admin/connectors/Popup";
|
import { usePopup } from "@/components/admin/connectors/Popup";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button } from "@/components/Button";
|
|
||||||
import { ThreeDotsLoader } from "@/components/Loading";
|
import { ThreeDotsLoader } from "@/components/Loading";
|
||||||
import { useConnectorCredentialIndexingStatus, useUsers } from "@/lib/hooks";
|
import { useConnectorCredentialIndexingStatus, useUsers } from "@/lib/hooks";
|
||||||
import { useUserGroups } from "./hooks";
|
import { useUserGroups } from "./hooks";
|
||||||
|
import { AdminPageTitle } from "@/components/admin/Title";
|
||||||
|
import { Button, Divider } from "@tremor/react";
|
||||||
|
|
||||||
const Main = () => {
|
const Main = () => {
|
||||||
const { popup, setPopup } = usePopup();
|
const { popup, setPopup } = usePopup();
|
||||||
@ -48,13 +49,20 @@ const Main = () => {
|
|||||||
<>
|
<>
|
||||||
{popup}
|
{popup}
|
||||||
<div className="my-3">
|
<div className="my-3">
|
||||||
<Button onClick={() => setShowForm(true)}>Create New User Group</Button>
|
<Button size="xs" color="green" onClick={() => setShowForm(true)}>
|
||||||
|
Create New User Group
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
{data.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<Divider />
|
||||||
<UserGroupsTable
|
<UserGroupsTable
|
||||||
userGroups={data}
|
userGroups={data}
|
||||||
setPopup={setPopup}
|
setPopup={setPopup}
|
||||||
refresh={refreshUserGroups}
|
refresh={refreshUserGroups}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{showForm && (
|
{showForm && (
|
||||||
<UserGroupCreationForm
|
<UserGroupCreationForm
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
@ -73,10 +81,10 @@ const Main = () => {
|
|||||||
const Page = () => {
|
const Page = () => {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto container">
|
<div className="mx-auto container">
|
||||||
<div className="border-solid border-gray-600 border-b pb-2 mb-4 flex">
|
<AdminPageTitle
|
||||||
<GroupsIcon size={32} />
|
title="Manage Users Groups"
|
||||||
<h1 className="text-3xl font-bold pl-2">Manage Users Groups</h1>
|
icon={<GroupsIcon size={32} />}
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
<Main />
|
<Main />
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,6 @@ import { DatabaseIcon } from "@/components/icons/icons";
|
|||||||
export default function QueryHistoryPage() {
|
export default function QueryHistoryPage() {
|
||||||
return (
|
return (
|
||||||
<main className="pt-4 mx-auto container">
|
<main className="pt-4 mx-auto container">
|
||||||
|
|
||||||
<AdminPageTitle title="Query History" icon={<DatabaseIcon size={32} />} />
|
<AdminPageTitle title="Query History" icon={<DatabaseIcon size={32} />} />
|
||||||
|
|
||||||
<QueryHistoryTable />
|
<QueryHistoryTable />
|
||||||
|
@ -4,7 +4,7 @@ export function DeleteButton({
|
|||||||
onClick,
|
onClick,
|
||||||
disabled,
|
disabled,
|
||||||
}: {
|
}: {
|
||||||
onClick?: () => void;
|
onClick?: (event: React.MouseEvent<HTMLElement>) => void | Promise<void>;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user