+ >
+ );
+};
+
+export default AdminTable;
diff --git a/ui/src/components/table/admin-table/table-element/add-admin.tsx b/ui/src/components/table/admin-table/table-element/add-admin.tsx
new file mode 100644
index 00000000..d6e522cf
--- /dev/null
+++ b/ui/src/components/table/admin-table/table-element/add-admin.tsx
@@ -0,0 +1,50 @@
+import {Input} from '@/components/ui/input'
+import {Dispatch, SetStateAction} from 'react';
+import {Button} from '@/components/ui/button';
+import {addAdmin} from '@/actions/groupings-api';
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger
+} from '@/components/ui/tooltip';
+
+interface InputProps {
+ input: string;
+ setInput: Dispatch>;
+}
+
+const handleClick = (input: string) => {
+ //TODO: create a condition where the admin list is checked for the uhid/uhUuid the user entered in.
+ //TODO: if it does not exist in the admin list, add the new admin to the UH Groupings admin list.
+ addAdmin(input);
+};
+const AddAdmin = ({input, setInput}: InputProps) => (
+ //Add tooltip
+
+ setInput(e.target.value)}
+ />
+
+
+
+
+
+
+ Add Member
+
+ You are about to add the following member to the admins list.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/*second column*/}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Are you sure you want to add {name} to the admins list?
+
+
+
+
+ Membership changes made
+ may not take effect immediately.
+ Usually, 3-5 minutes should be
+ anticipated. In extreme cases changes
+ may take several hours to be fully
+ processed, depending on the number
+ of members and the synchronization
+ destination.
+
+
+
+
+
+ close()}>Cancel
+
+
+
+
+ );
+};
+
+export default AddAdminsDialog;
diff --git a/ui/src/components/table/admin-table/table-element/admin-table-columns.tsx b/ui/src/components/table/admin-table/table-element/admin-table-columns.tsx
new file mode 100644
index 00000000..49f9b40d
--- /dev/null
+++ b/ui/src/components/table/admin-table/table-element/admin-table-columns.tsx
@@ -0,0 +1,19 @@
+const AdminTableColumns = [
+ {
+ header: 'ADMIN NAME',
+ accessorKey: 'name'
+ },
+ {
+ header: 'UH NUMBER',
+ accessorKey: 'uhUuid'
+ },
+ {
+ header: 'UH USERNAME',
+ accessorKey: 'uid'
+ },
+ {
+ header: 'REMOVE',
+ }
+];
+
+export default AdminTableColumns;
diff --git a/ui/src/components/table/admin-table/table-element/remove-admins-dialog.tsx b/ui/src/components/table/admin-table/table-element/remove-admins-dialog.tsx
new file mode 100644
index 00000000..f791dd44
--- /dev/null
+++ b/ui/src/components/table/admin-table/table-element/remove-admins-dialog.tsx
@@ -0,0 +1,99 @@
+import {
+ AlertDialog, AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger
+} from '@/components/ui/alert-dialog';
+import { Button } from '@/components/ui/button';
+import { Alert, AlertDescription } from '@/components/ui/alert';
+import {Label} from '@/components/ui/label';
+import { Trash2Icon } from 'lucide-react';
+import {MemberResult} from '@/lib/types';
+import {removeAdmin} from '@/lib/actions';
+
+const RemoveAdminsDialog = ({uid, name, uhUuid} : MemberResult) => {
+
+ return (
+
+
+
+
+
+
+ Remove Member
+ {/**/}
+
+ You are about to remove the following member from the admins list.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/*second column*/}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Are you sure you want to remove {name} from the admins list?
+
+
+
+
+ Membership changes made
+ may not take effect immediately.
+ Usually, 3-5 minutes should be
+ anticipated. In extreme cases changes
+ may take several hours to be fully
+ processed, depending on the number
+ of members and the synchronization
+ destination.
+
+
+
+
+
+ close()}>Cancel
+
+
+
+ );
+};
+
+export default RemoveAdminsDialog;
diff --git a/ui/src/components/table/admin-table/table-element/remove-admins-icon.tsx b/ui/src/components/table/admin-table/table-element/remove-admins-icon.tsx
new file mode 100644
index 00000000..468b7912
--- /dev/null
+++ b/ui/src/components/table/admin-table/table-element/remove-admins-icon.tsx
@@ -0,0 +1,25 @@
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
+import { Trash2Icon } from 'lucide-react';
+import { useState } from 'react';
+
+const RemoveAdminsIcon = () => {
+ const [tooltipContent] = useState('Remove this administrator (there must be at least 1 remaining).');
+ const [tooltipVisible, setTooltipVisible] = useState(false);
+
+ return (
+
+
+
+
+
+
+
+
{tooltipContent}
+
+
+
+
+ );
+};
+
+export default RemoveAdminsIcon;
diff --git a/ui/src/components/table/groupings-table-skeleton.tsx b/ui/src/components/table/groupings-table/groupings-table-skeleton.tsx
similarity index 95%
rename from ui/src/components/table/groupings-table-skeleton.tsx
rename to ui/src/components/table/groupings-table/groupings-table-skeleton.tsx
index 295a3221..f97260ee 100644
--- a/ui/src/components/table/groupings-table-skeleton.tsx
+++ b/ui/src/components/table/groupings-table/groupings-table-skeleton.tsx
@@ -1,6 +1,6 @@
+import GroupingsTableColumns from '@/components/table/groupings-table/table-element/groupings-table-columns';
import { Skeleton } from '@/components/ui/skeleton';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
-import GroupingsTableColumns from '@/components/table/table-element/groupings-table-columns';
const GroupingsTableSkeleton = () => {
const pageSize = 7; // Average number of rows
diff --git a/ui/src/components/table/groupings-table.tsx b/ui/src/components/table/groupings-table/groupings-table.tsx
similarity index 93%
rename from ui/src/components/table/groupings-table.tsx
rename to ui/src/components/table/groupings-table/groupings-table.tsx
index 241a8220..a0c7331a 100644
--- a/ui/src/components/table/groupings-table.tsx
+++ b/ui/src/components/table/groupings-table/groupings-table.tsx
@@ -12,13 +12,13 @@ import {
} from '@tanstack/react-table';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import ColumnSettings from '@/components/table/table-element/column-settings';
+import GroupingsTableColumns from '@/components/table/groupings-table/table-element/groupings-table-columns';
import PaginationBar from '@/components/table/table-element/pagination-bar';
import GlobalFilter from '@/components/table/table-element/global-filter';
import SortArrow from '@/components/table/table-element/sort-arrow';
import { useState } from 'react';
import { useLocalStorage } from 'usehooks-ts';
import { GroupingPath } from '@/lib/types';
-import GroupingsTableColumns from '@/components/table/table-element/groupings-table-columns';
const pageSize = parseInt(process.env.NEXT_PUBLIC_PAGE_SIZE as string);
@@ -50,13 +50,13 @@ const GroupingsTable = ({ groupingPaths }: { groupingPaths: GroupingPath[] }) =>
Manage Groupings
-
+
-
+
{table.getHeaderGroups().map((headerGroup) => (
diff --git a/ui/src/components/table/table-element/ grouping-description-cell.tsx b/ui/src/components/table/groupings-table/table-element/grouping-description-cell.tsx
similarity index 100%
rename from ui/src/components/table/table-element/ grouping-description-cell.tsx
rename to ui/src/components/table/groupings-table/table-element/grouping-description-cell.tsx
diff --git a/ui/src/components/table/table-element/ grouping-name-cell.tsx b/ui/src/components/table/groupings-table/table-element/grouping-name-cell.tsx
similarity index 100%
rename from ui/src/components/table/table-element/ grouping-name-cell.tsx
rename to ui/src/components/table/groupings-table/table-element/grouping-name-cell.tsx
diff --git a/ui/src/components/table/table-element/grouping-path-cell.tsx b/ui/src/components/table/groupings-table/table-element/grouping-path-cell.tsx
similarity index 100%
rename from ui/src/components/table/table-element/grouping-path-cell.tsx
rename to ui/src/components/table/groupings-table/table-element/grouping-path-cell.tsx
diff --git a/ui/src/components/table/table-element/groupings-table-columns.tsx b/ui/src/components/table/groupings-table/table-element/groupings-table-columns.tsx
similarity index 73%
rename from ui/src/components/table/table-element/groupings-table-columns.tsx
rename to ui/src/components/table/groupings-table/table-element/groupings-table-columns.tsx
index dc73130e..1c4e025c 100644
--- a/ui/src/components/table/table-element/groupings-table-columns.tsx
+++ b/ui/src/components/table/groupings-table/table-element/groupings-table-columns.tsx
@@ -1,8 +1,8 @@
import { ColumnDef } from '@tanstack/react-table';
import { GroupingPath } from '@/lib/types';
-import GroupingPathCell from '@/components/table/table-element/grouping-path-cell';
-import GroupingDescriptionCell from '@/components/table/table-element/ grouping-description-cell';
-import GroupingNameCell from '@/components/table/table-element/ grouping-name-cell';
+import GroupingPathCell from '@/components/table/groupings-table/table-element/grouping-path-cell';
+import GroupingDescriptionCell from '@/components/table/groupings-table/table-element/grouping-description-cell';
+import GroupingNameCell from '@/components/table/groupings-table/table-element/grouping-name-cell';
const GroupingsTableColumns: ColumnDef[] = [
{
diff --git a/ui/src/components/table/table-element/global-filter.tsx b/ui/src/components/table/table-element/global-filter.tsx
index 2977f36f..02428287 100644
--- a/ui/src/components/table/table-element/global-filter.tsx
+++ b/ui/src/components/table/table-element/global-filter.tsx
@@ -1,8 +1,8 @@
import { Input } from '@/components/ui/input';
import { Dispatch, SetStateAction } from 'react';
-const GlobalFilter = ({ filter, setFilter }: { filter: string; setFilter: Dispatch> }) => (
- setFilter(e.target.value)} />
+const GlobalFilter = ({ placeholder, filter, setFilter }: { placeholder: string; filter: string; setFilter: Dispatch> }) => (
+ setFilter(e.target.value)} />
);
export default GlobalFilter;
diff --git a/ui/src/components/ui/dialog.tsx b/ui/src/components/ui/dialog.tsx
new file mode 100644
index 00000000..45e183a2
--- /dev/null
+++ b/ui/src/components/ui/dialog.tsx
@@ -0,0 +1,101 @@
+'use client';
+
+import * as React from 'react';
+import * as DialogPrimitive from '@radix-ui/react-dialog';
+import { X } from 'lucide-react';
+
+import { cn } from '@/components/ui/utils';
+
+const Dialog = DialogPrimitive.Root;
+
+const DialogTrigger = DialogPrimitive.Trigger;
+
+const DialogPortal = DialogPrimitive.Portal;
+
+const DialogClose = DialogPrimitive.Close;
+
+const DialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
+
+const DialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+));
+DialogContent.displayName = DialogPrimitive.Content.displayName;
+
+const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => (
+
+);
+DialogHeader.displayName = 'DialogHeader';
+
+const DialogFooter = ({ className, ...props }: React.HTMLAttributes) => (
+
+);
+DialogFooter.displayName = 'DialogFooter';
+
+const DialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogTitle.displayName = DialogPrimitive.Title.displayName;
+
+const DialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogDescription.displayName = DialogPrimitive.Description.displayName;
+
+export {
+ Dialog,
+ DialogPortal,
+ DialogOverlay,
+ DialogClose,
+ DialogTrigger,
+ DialogContent,
+ DialogHeader,
+ DialogFooter,
+ DialogTitle,
+ DialogDescription
+};
diff --git a/ui/src/components/ui/pagination.tsx b/ui/src/components/ui/pagination.tsx
index 8c24a1f3..b1d13e76 100644
--- a/ui/src/components/ui/pagination.tsx
+++ b/ui/src/components/ui/pagination.tsx
@@ -57,6 +57,7 @@ const PaginationLink = ({
{...props}
/>
)
+
PaginationLink.displayName = 'PaginationLink'
const PaginationPrevious = ({
diff --git a/ui/src/lib/actions.ts b/ui/src/lib/actions.ts
index 6d1fe340..dbf13e03 100644
--- a/ui/src/lib/actions.ts
+++ b/ui/src/lib/actions.ts
@@ -12,6 +12,8 @@ import {
GroupingRemoveResult,
GroupingRemoveResults,
GroupingUpdateDescriptionResult,
+ GroupingGroupMembers,
+ GroupingPaths,
MemberAttributeResults
} from './types';
import {
@@ -20,6 +22,7 @@ import {
postRequest,
postRequestAsync,
putRequest,
+ getRequest,
putRequestAsync
} from './http-client';
@@ -46,6 +49,27 @@ export const updateDescription = async (
return putRequest(endpoint, currentUser.uid, description);
};
+/**
+ * Get a list of admins.
+ *
+ * @returns The promise of the grouping admins or ApiError type
+ */
+export const groupingAdmins = async (): Promise => {
+ const currentUser = await getCurrentUser();
+ const endpoint = `${baseUrl}/groupings/admins`;
+ return getRequest(endpoint, currentUser.uid);
+}
+
+/**
+ * Get a list of all grouping paths.
+ *
+ * @returns The promise of all the grouping paths or ApiError type
+ */
+export const getAllGroupings = async (): Promise => {
+ const currentUser = await getCurrentUser();
+ const endpoint = `${baseUrl}/groupings`;
+ return getRequest(endpoint, currentUser.uid);
+}
/**
* Add members to the include group of a grouping.
*
diff --git a/ui/src/lib/fetchers.ts b/ui/src/lib/fetchers.ts
index ff399a99..1f2aaea5 100644
--- a/ui/src/lib/fetchers.ts
+++ b/ui/src/lib/fetchers.ts
@@ -99,7 +99,7 @@ export const groupingOptAttributes = async (groupingPath: string): Promise => {
const currentUser = await getCurrentUser();
- const endpoint = `${baseUrl}/grouping-admins`;
+ const endpoint = `${baseUrl}/groupings/admins`;
return getRequest(endpoint, currentUser.uid);
};
@@ -110,7 +110,7 @@ export const groupingAdmins = async (): Promise
*/
export const getAllGroupings = async (): Promise => {
const currentUser = await getCurrentUser();
- const endpoint = `${baseUrl}/all-groupings`;
+ const endpoint = `${baseUrl}/groupings`;
return getRequest(endpoint, currentUser.uid);
};
diff --git a/ui/tailwind.config.ts b/ui/tailwind.config.ts
index 61ef64fd..1d5e3e0a 100644
--- a/ui/tailwind.config.ts
+++ b/ui/tailwind.config.ts
@@ -61,4 +61,4 @@ const config = {
plugins: [require('tailwindcss-animate')]
} satisfies Config;
-export default config;
+export default config;
\ No newline at end of file
diff --git a/ui/tests/components/table/groupings-table.test.tsx b/ui/tests/components/table/groupings-table/groupings-table.test.tsx
similarity index 98%
rename from ui/tests/components/table/groupings-table.test.tsx
rename to ui/tests/components/table/groupings-table/groupings-table.test.tsx
index 0ff91e8e..a41d2f57 100644
--- a/ui/tests/components/table/groupings-table.test.tsx
+++ b/ui/tests/components/table/groupings-table/groupings-table.test.tsx
@@ -1,5 +1,5 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
-import GroupingsTable from '@/components/table/groupings-table';
+import GroupingsTable from '@/components/table/groupings-table/groupings-table';
import userEvent from '@testing-library/user-event';
const pageSize = parseInt(process.env.NEXT_PUBLIC_PAGE_SIZE as string);
diff --git a/ui/tests/components/table/table-element/grouping-description-cell.test.tsx b/ui/tests/components/table/groupings-table/table-element/grouping-description-cell.test.tsx
similarity index 90%
rename from ui/tests/components/table/table-element/grouping-description-cell.test.tsx
rename to ui/tests/components/table/groupings-table/table-element/grouping-description-cell.test.tsx
index cd38b1ef..684acf8a 100644
--- a/ui/tests/components/table/table-element/grouping-description-cell.test.tsx
+++ b/ui/tests/components/table/groupings-table/table-element/grouping-description-cell.test.tsx
@@ -1,6 +1,6 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import GroupingDescriptionCell from '@/components/table/table-element/ grouping-description-cell';
+import GroupingDescriptionCell from '@/components/table/groupings-table/table-element/grouping-description-cell';
describe('GroupingDescriptionCell', () => {
it('renders the description inside TooltipOnTruncate', () => {
diff --git a/ui/tests/components/table/table-element/grouping-name-cell.test.tsx b/ui/tests/components/table/groupings-table/table-element/grouping-name-cell.test.tsx
similarity index 84%
rename from ui/tests/components/table/table-element/grouping-name-cell.test.tsx
rename to ui/tests/components/table/groupings-table/table-element/grouping-name-cell.test.tsx
index 218783bf..c6533cd3 100644
--- a/ui/tests/components/table/table-element/grouping-name-cell.test.tsx
+++ b/ui/tests/components/table/groupings-table/table-element/grouping-name-cell.test.tsx
@@ -1,5 +1,5 @@
import { render, screen} from '@testing-library/react';
-import GroupingNameCell from '@/components/table/table-element/ grouping-name-cell';
+import GroupingNameCell from '@/components/table/groupings-table/table-element/grouping-name-cell';
describe('GroupingNameCell', () => {
it('renders the link with the correct path and displays the name', () => {
diff --git a/ui/tests/components/table/table-element/grouping-path-cell.test.tsx b/ui/tests/components/table/groupings-table/table-element/grouping-path-cell.test.tsx
similarity index 96%
rename from ui/tests/components/table/table-element/grouping-path-cell.test.tsx
rename to ui/tests/components/table/groupings-table/table-element/grouping-path-cell.test.tsx
index 0b5ebe4e..83351534 100644
--- a/ui/tests/components/table/table-element/grouping-path-cell.test.tsx
+++ b/ui/tests/components/table/groupings-table/table-element/grouping-path-cell.test.tsx
@@ -1,5 +1,5 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
-import GroupingPathCell from '@/components/table/table-element/grouping-path-cell';
+import GroupingPathCell from '@/components/table/groupings-table/table-element/grouping-path-cell';
import userEvent from '@testing-library/user-event';
describe('GroupingPathCell', () => {
diff --git a/ui/tests/components/table/table-element/global-filter.test.tsx b/ui/tests/components/table/table-element/global-filter.test.tsx
index 16c78528..5de9623d 100644
--- a/ui/tests/components/table/table-element/global-filter.test.tsx
+++ b/ui/tests/components/table/table-element/global-filter.test.tsx
@@ -5,12 +5,12 @@ describe('GlobalFilter', () => {
const mockSetFilter = jest.fn();
it('renders the input with correct placeholder and value', () => {
- render();
+ render();
expect(screen.getByPlaceholderText('Filter Groupings...')).toHaveValue('test');
});
it('renders call setFilter when the input value changes', () => {
- render();
+ render();
fireEvent.change(screen.getByPlaceholderText('Filter Groupings...'), { target: { value: 'new test' } });
expect(mockSetFilter).toHaveBeenCalledWith('new test');
diff --git a/ui/tests/lib/fetchers.test.ts b/ui/tests/lib/fetchers.test.ts
index 6ea82ae5..3a1ac66e 100644
--- a/ui/tests/lib/fetchers.test.ts
+++ b/ui/tests/lib/fetchers.test.ts
@@ -52,7 +52,7 @@ describe('fetchers', () => {
describe('getAllGroupings', () => {
it('should make a GET request at the correct endpoint', async () => {
await getAllGroupings();
- expect(fetch).toHaveBeenCalledWith(`${baseUrl}/all-groupings`, {
+ expect(fetch).toHaveBeenCalledWith(`${baseUrl}/groupings`, {
headers: { current_user: currentUser.uid }
});
});
@@ -220,7 +220,7 @@ describe('fetchers', () => {
describe('groupingAdmins', () => {
it('should make a GET request at the correct endpoint', async () => {
await groupingAdmins();
- expect(fetch).toHaveBeenCalledWith(`${baseUrl}/grouping-admins`, {
+ expect(fetch).toHaveBeenCalledWith(`${baseUrl}/groupings/admins`, {
headers: { current_user: currentUser.uid }
});
});