mirror of
https://github.com/Mintplex-Labs/vector-admin.git
synced 2026-07-19 13:16:03 -04:00
@@ -74,7 +74,6 @@ export default function OrganizationSettingsView() {
|
||||
<SyncConnectorModal
|
||||
organization={organization}
|
||||
connector={connector}
|
||||
onUpdate={(newConnector) => setConnector(newConnector)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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 →</p>
|
||||
</a>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 && (
|
||||
|
||||
Reference in New Issue
Block a user