Merge pull request #98 from Mintplex-Labs/v2-members

v2 members
This commit is contained in:
Timothy Carambat
2023-12-21 11:57:12 -08:00
committed by GitHub
4 changed files with 1214 additions and 79 deletions
@@ -74,7 +74,6 @@ export default function OrganizationSettingsView() {
<SyncConnectorModal
organization={organization}
connector={connector}
onUpdate={(newConnector) => setConnector(newConnector)}
/>
</div>
</div>
+22 -18
View File
@@ -2,23 +2,27 @@ import { useState } from 'react';
import { Info } from 'react-feather';
import { APP_NAME } from '../../../utils/constants';
import paths from '../../../utils/paths';
import { CaretDown } from '@phosphor-icons/react';
export default function ToolsList({ organization }: { organization: any }) {
return (
<div className="col-span-12 flex-1 rounded-sm bg-white pb-6 xl:col-span-4">
<div className="flex items-start justify-between">
<div className="mb-6 px-6">
<h4 className="text-3xl font-semibold text-black dark:text-white">
Advanced management tools
</h4>
<p className="text-gray-600">
below are a list of advanced {APP_NAME} only tools and services that
you can use to manage your connected vector database.
</p>
</div>
<div className="col-span-12 h-screen flex-1 rounded-sm bg-main pb-6 xl:col-span-4">
<div className="-mt-10 flex items-center gap-x-4">
<button
onClick={() => window.history.back()}
className="flex h-[34px] w-[34px] rotate-90 items-center justify-center rounded-full border border-transparent bg-zinc-900 text-white transition-all duration-300 hover:border-white/20 hover:bg-opacity-5 hover:text-white"
>
<CaretDown weight="bold" size={18} />
</button>
<div className="text-lg font-medium text-white">Organization Tools</div>
</div>
<div className="px-6">
<div className="ml-13 pr-6">
<div className="mt-1 w-125 text-sm text-white text-opacity-60">
Below are a list of advanced {APP_NAME} only tools and services that
you can use to manage your connected vector database.
</div>
<ToolItem
title="Automatic context drift detection"
description={`Catch "context drift" by detecting changes in your vector databases similarity searches before they cause problems.`}
@@ -64,25 +68,25 @@ const ToolItem = ({
}) => {
const [show, setShow] = useState(false);
return (
<div className="flex w-full items-center justify-between border-b border-gray-200 py-5 text-left text-gray-500 dark:border-gray-700 dark:text-gray-400">
<div className="flex w-full items-center justify-between border-b border-white/20 py-5 text-left ">
<div className="flex w-full items-center justify-between pr-4">
<div className="flex items-center gap-x-8">
<div className="flex flex-col gap-y-2">
<span className="text-xl font-bold text-gray-800">{title}</span>
<span className="font-regular rounded-full py-1 text-sm text-gray-500">
<span className="text-sm font-medium text-white ">{title}</span>
<span className="rounded-full text-sm text-white/60">
{description}
</span>
</div>
</div>
{!available ? (
<div className="flex items-center gap-x-1 rounded-md bg-gray-100 px-4 py-1.5 text-slate-600 ">
<div className="flex items-center gap-x-1 rounded-md px-4 py-1.5 text-white/60 ">
<Info size={14} />
<p className="text-sm font-normal">feature under development.</p>
<p className="text-sm font-normal">Feature under development.</p>
</div>
) : (
<a
href={linkTo}
className="flex items-center gap-x-1 rounded-md bg-blue-600 px-4 py-1.5 text-slate-600 text-white hover:bg-blue-700 "
className="flex items-center gap-x-1 rounded-md px-2 py-1.5 text-sky-400 hover:bg-white"
>
<p className="text-sm font-normal ">Open tool &rarr;</p>
</a>
File diff suppressed because it is too large Load Diff
+152 -59
View File
@@ -1,7 +1,8 @@
import { lazy, memo } from 'react';
import { lazy, memo, useEffect, useRef, useState } from 'react';
import moment from 'moment';
import pluralize from 'pluralize';
import { APP_NAME } from '../../../utils/constants';
import { CaretDown, DotsThreeVertical, Plus } from '@phosphor-icons/react';
import truncate from 'truncate';
import { debounce } from 'lodash';
const DeleteUserConfirmation = lazy(() => import('./DeleteUserConfirmation'));
const EditUserConfirmation = lazy(() => import('./EditUserConfirmation'));
@@ -14,36 +15,29 @@ export default function UserList({
}) {
return (
<>
<div className="col-span-12 flex-1 rounded-sm dark:border-strokedark dark:bg-boxdark xl:col-span-4">
<div className="col-span-12 -mt-18 h-screen flex-1 rounded-sm xl:col-span-4">
<div className="flex items-start justify-between">
<div className="mb-6 flex flex-col gap-y-1 px-7.5 ">
<div className="flex items-center gap-x-2">
<h4 className="text-3xl font-semibold text-black dark:text-white">
{APP_NAME} Users
</h4>
<button
type="button"
onClick={() =>
document.getElementById(`new-user-modal`)?.showModal()
}
className="rounded-lg px-4 py-2 text-sm text-blue-400 hover:bg-blue-50 hover:text-blue-600"
>
Add New User
</button>
<div className="-mt-10 flex items-center gap-x-4">
<button
onClick={() => window.history.back()}
className="flex h-[34px] w-[34px] rotate-90 items-center justify-center rounded-full border border-transparent bg-zinc-900 text-white transition-all duration-300 hover:border-white/20 hover:bg-opacity-5 hover:text-white"
>
<CaretDown weight="bold" size={18} />
</button>
<div className="z-10 text-lg font-medium text-white">
Members
</div>
</div>
</div>
<p className="text-sm text-slate-500">
{users?.length} {pluralize('user', users.length)}
</p>
</div>
</div>
<div className="px-6">
<table className="w-full text-left text-sm text-gray-500 dark:text-gray-400">
<thead className="bg-gray-50 text-xs uppercase text-gray-700 dark:bg-gray-700 dark:text-gray-400">
<table className="w-full text-left text-sm text-white">
<thead className="border-b-2 border-white/10 bg-main text-sm font-medium uppercase text-white/50">
<tr>
<th scope="col" className="px-6 py-3">
#
</th>
<th scope="col" className="px-6 py-3">
Email
</th>
@@ -51,13 +45,13 @@ export default function UserList({
Role
</th>
<th scope="col" className="px-6 py-3">
Memberships
Organizations
</th>
<th scope="col" className="px-6 py-3">
Created
Joined
</th>
<th scope="col" className="px-6 py-3">
Actions
{' '}
</th>
</tr>
</thead>
@@ -73,6 +67,18 @@ export default function UserList({
})}
</tbody>
</table>
<button
type="button"
onClick={() =>
document.getElementById(`new-user-modal`)?.showModal()
}
className="ml-3 rounded-lg px-2 py-2 text-sm text-sky-400 hover:bg-sky-50"
>
<div className="flex items-center gap-x-2">
<Plus size={18} weight="bold" />
<div className="font-semibold">Add New User</div>
</div>
</button>
</div>
</div>
</>
@@ -81,52 +87,139 @@ export default function UserList({
const UserItem = memo(
({ user, organizations }: { user: any; organizations: any[] }) => {
const [showMenu, setShowMenu] = useState(false);
const [showTooltip, setShowTooltip] = useState(false);
const menuRef = useRef(null);
const buttonRef = useRef();
const tooltipRef = useRef();
const debounceHideTooltip = useRef(
debounce(() => setShowTooltip(false), 200)
).current;
const handleMouseEnterTooltip = () => {
debounceHideTooltip.cancel();
setShowTooltip(true);
};
const handleMouseLeaveTooltip = () => {
debounceHideTooltip();
};
useEffect(() => {
return () => debounceHideTooltip.cancel();
}, [debounceHideTooltip]);
useEffect(() => {
function handleClickOutside(event) {
if (
menuRef.current &&
!menuRef.current.contains(event.target) &&
!buttonRef.current.contains(event.target)
) {
setShowMenu(false);
}
if (tooltipRef.current && !tooltipRef.current.contains(event.target)) {
setShowTooltip(false);
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
const firstOrganization = user.memberships[0]?.name;
const additionalOrganizationsCount = user.memberships.length - 1;
return (
<>
<tr
id={`user-row-${user.id}`}
className="border-b bg-white transition-all duration-300 dark:border-gray-700 dark:bg-gray-800"
className="h-[46px] text-sm font-semibold text-white transition-all duration-300 hover:bg-white/5"
>
<th
scope="row"
className="whitespace-nowrap px-6 py-4 font-medium text-gray-900 dark:text-white"
>
{user.id}
</th>
<td className="px-6 py-4">{user.email}</td>
<td className="px-6 py-4">{user.role}</td>
<td className="px-6 py-4 font-light capitalize">{user.role}</td>
<td className="px-6 py-4">
<div className="no-scrollbar flex w-[200px] flex-row items-center gap-x-2 overflow-x-scroll">
{user.memberships.map((org: any) => {
return (
<div className="w-fit whitespace-nowrap rounded-full bg-blue-100 px-5 py-1 text-sm text-blue-800">
{org.name}
<div className="flex items-center gap-x-2">
<div className="whitespace-nowrap rounded-full bg-white/10 px-2.5 py-0.5 text-sm font-light text-white">
{firstOrganization}
</div>
{additionalOrganizationsCount > 0 && (
<div className="relative">
<div
onMouseEnter={handleMouseEnterTooltip}
onMouseLeave={handleMouseLeaveTooltip}
className="cursor-pointer whitespace-nowrap rounded-full bg-white/10 px-2.5 py-0.5 text-sm font-light text-white"
>
+{additionalOrganizationsCount}
</div>
);
})}
{showTooltip && (
<div
ref={tooltipRef}
onMouseEnter={handleMouseEnterTooltip}
onMouseLeave={handleMouseLeaveTooltip}
className="absolute left-0 z-10 mt-2 max-h-[250px] w-48 overflow-y-auto rounded-lg border border-white/20 bg-main px-3 py-2 shadow-lg"
>
{user.memberships.slice(1).map((org, index) => (
<div
key={index}
className="my-2 whitespace-nowrap rounded-full bg-white/10 px-2.5 py-0.5 text-sm font-light text-white"
>
{truncate(org.name, 18)}
</div>
))}
</div>
)}
</div>
)}
</div>
</td>
<td className="px-6 py-4">{moment(user?.createdAt).fromNow()}</td>
<td className="flex items-center gap-x-4 px-6 py-4">
<td className="px-6 py-4 font-light">
{moment(user?.createdAt).fromNow()}
</td>
<td className="relative flex items-center justify-center gap-x-4 px-6 py-4">
<button
type="button"
onClick={() => {
document.getElementById(`user-${user.id}-edit`)?.showModal();
}}
className="rounded-lg px-2 py-1 text-blue-400 transition-all duration-300 hover:bg-blue-50 hover:text-blue-600"
ref={buttonRef}
onClick={() => setShowMenu(!showMenu)}
className={`rounded-lg p-1 text-white transition-all duration-300 hover:bg-white/10 ${
showMenu && 'bg-white/10'
}`}
>
Edit
</button>
<button
type="button"
onClick={() => {
document.getElementById(`user-${user.id}-delete`)?.showModal();
}}
className="rounded-lg px-2 py-1 text-red-400 transition-all duration-300 hover:bg-red-50 hover:text-red-600"
>
Delete
<DotsThreeVertical size={24} weight="bold" />
</button>
{showMenu && (
<div
ref={menuRef}
className="absolute right-11 top-full -mt-2 flex w-fit rounded-lg border border-white/20 bg-main p-4"
>
<div className="flex flex-col gap-y-2">
<button
onClick={() => {
document
.getElementById(`user-${user.id}-edit`)
?.showModal();
}}
type="button"
className="w-full whitespace-nowrap rounded-md px-4 py-1.5 text-left text-white hover:bg-slate-200/20"
>
Edit
</button>
<button
onClick={() => {
document
.getElementById(`user-${user.id}-delete`)
?.showModal();
}}
type="button"
className="w-full whitespace-nowrap rounded-md px-4 py-1.5 text-left text-white hover:bg-slate-200/20"
>
Delete
</button>
</div>
</div>
)}
</td>
</tr>
{!!user && (