Skip to content

Commit

Permalink
Implement Manage Groupings
Browse files Browse the repository at this point in the history
  • Loading branch information
hokwaichan committed Nov 2, 2024
1 parent c6b357b commit 3024a2b
Show file tree
Hide file tree
Showing 43 changed files with 766 additions and 13 deletions.
9 changes: 9 additions & 0 deletions ui/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ const nextConfig = {
basePath: '/uhgroupings',
experimental: {
serverComponentsExternalPackages: ['camaro']
},

rewrites: async () => {
return [
{
source: '/groupings/:groupingPath',
destination: '/groupings/:groupingPath/all-members'
}
];
}
};

Expand Down
1 change: 0 additions & 1 deletion ui/public/id-card-solid.svg

This file was deleted.

1 change: 0 additions & 1 deletion ui/public/key-solid.svg

This file was deleted.

1 change: 0 additions & 1 deletion ui/public/user-solid.svg

This file was deleted.

1 change: 0 additions & 1 deletion ui/public/wrench-solid.svg

This file was deleted.

4 changes: 2 additions & 2 deletions ui/src/app/(home)/_components/after-login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ const AfterLogin = async () => {
<div key={index} className="flex flex-col justify-between">
<div>
<div className="flex items-center mb-1">
<FontAwesomeIcon icon={pageInfoItem.icon} style={{ width: `${pageInfoItem.width}px`, height: `${pageInfoItem.height}px` }} className="mr-5 mb-4 max-w-${pageInfoItem.icon.width} h-auto" aria-label={pageInfoItem.ariaLabel} />
<FontAwesomeIcon icon={pageInfoItem.icon} style={{ width: `${pageInfoItem.width}px`, height: `${pageInfoItem.height}px` }} className="mr-5 mb-4 max-w-${pageInfoItem.icon.width} h-auto text-text-primary" aria-label={pageInfoItem.ariaLabel} />
{pageInfoItem.number !== null && (
<span className="text-[2.5rem] text-text-color">{pageInfoItem.number}</span>
<span className="text-[2.5rem] text-text-color ">{pageInfoItem.number}</span>
)}
</div>
<h2 className="text-2xl text-text-color font-medium mb-2">{pageInfoItem.title}</h2>
Expand Down
9 changes: 9 additions & 0 deletions ui/src/app/groupings/[groupingPath]/@tab/actions/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const ActionsTab = () => {
return (
<h1 className="font-bold text-gray-900 font-weight:900 pt-2 mb-0 inline-block text-3xl pl-2.5">
Grouping Actions
</h1>
);
};

export default ActionsTab;
7 changes: 7 additions & 0 deletions ui/src/app/groupings/[groupingPath]/@tab/all-members/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const AllMembersTab = () => {
return (
<h1 className="font-bold text-gray-900 font-weight:900 pt-2 mb-0 inline-block text-3xl pl-2.5">All Members</h1>
);
};

export default AllMembersTab;
9 changes: 9 additions & 0 deletions ui/src/app/groupings/[groupingPath]/@tab/basis/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const BasisTab = () => {
return (
<h1 className="font-bold text-gray-900 font-weight:900 pt-2 mb-0 inline-block text-3xl pl-2.5">
Basis Members
</h1>
);
};

export default BasisTab;
5 changes: 5 additions & 0 deletions ui/src/app/groupings/[groupingPath]/@tab/exclude/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const ExcludeTab = () => {
return <h1 className="font-bold text-gray-900 font-weight:900 pt-2 mb-0 inline-block text-3xl pl-2.5">Exclude</h1>;
};

export default ExcludeTab;
5 changes: 5 additions & 0 deletions ui/src/app/groupings/[groupingPath]/@tab/include/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const IncludeTab = () => {
return <h1 className="font-bold text-gray-900 font-weight:900 pt-2 mb-0 inline-block text-3xl pl-2.5">Include</h1>;
};

export default IncludeTab;
5 changes: 5 additions & 0 deletions ui/src/app/groupings/[groupingPath]/@tab/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const TabLayout = ({ children }: { children: React.ReactNode }) => {
return <>{children}</>;
};

export default TabLayout;
5 changes: 5 additions & 0 deletions ui/src/app/groupings/[groupingPath]/@tab/owners/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const OwnersTab = () => {
return <h1 className="font-bold text-gray-900 font-weight:900 pt-2 mb-0 inline-block text-3xl pl-2.5">Owners</h1>;
};

export default OwnersTab;
7 changes: 7 additions & 0 deletions ui/src/app/groupings/[groupingPath]/@tab/preferences/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const PreferencesTab = () => {
return (
<h1 className="font-bold text-gray-900 font-weight:900 pt-2 mb-0 inline-block text-3xl pl-2.5">Preferences</h1>
);
};

export default PreferencesTab;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const SyncDestinationsTab = () => {
return (
<h1 className="font-bold text-gray-900 font-weight:900 pt-2 mb-0 inline-block text-3xl pl-2.5">
Synchronization Destinations
</h1>
);
};

export default SyncDestinationsTab;
157 changes: 157 additions & 0 deletions ui/src/app/groupings/[groupingPath]/_components/description-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
'use client';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faEdit, faCircleCheck, faTimesCircle } from '@fortawesome/free-regular-svg-icons';
import { updateDescription } from '@/lib/actions';
import { Alert } from '@/components/ui/alert';
import { SubmitHandler, useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import { useReducer } from 'react';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { useRouter } from 'next/navigation';

const DescriptionForm = ({ groupDescription, groupPath }: { groupDescription: string; groupPath: string }) => {
const router = useRouter();
type State = { isFormVisible: boolean; description: string };
type Action = { type: 'TOGGLE_FORM' } | { type: 'CLOSE_FORM' } | { type: 'UPDATE_DESCRIPTION'; payload: string };

const initialState: State = { isFormVisible: false, description: groupDescription };

const reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'TOGGLE_FORM':
return { ...state, isFormVisible: !state.isFormVisible };
case 'CLOSE_FORM':
return { ...state, isFormVisible: false };
case 'UPDATE_DESCRIPTION':
return { ...state, description: action.payload };
}
};

const [state, dispatch] = useReducer(reducer, initialState);

const schema = z.object({
description: z.string()
});

const {
register,
handleSubmit,
reset,
formState: { isSubmitting },
watch
} = useForm<z.infer<typeof schema>>({
defaultValues: {
description: groupDescription
},
resolver: zodResolver(schema)
});

const onSubmit: SubmitHandler<z.infer<typeof schema>> = async (data) => {
const finalDescription =
data.description.trim() === '' ? 'No description given for this Grouping.' : data.description;
await updateDescription(finalDescription, groupPath);
dispatch({ type: 'UPDATE_DESCRIPTION', payload: finalDescription });
dispatch({ type: 'CLOSE_FORM' });
router.refresh();
};

const currentDescription = watch('description', state.description);

const closeForm = () => {
reset({ description: state.description });
dispatch({ type: 'CLOSE_FORM' });
};

return (
<div>
<p className="text-gray-100 mb-0 break-words">
<b>Description:</b> {state.description} &nbsp;
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<button
onClick={() => dispatch({ type: 'TOGGLE_FORM' })}
className="text-white mr-2 font-normal bg-uh-teal text-center rounded-none"
aria-label="edit"
>
<FontAwesomeIcon icon={faEdit} className="w-5 h-4" />
</button>
</TooltipTrigger>
<TooltipContent className="bg-black text-white max-w-48 text-center border-none">
Edit this grouping&apos;s description
</TooltipContent>
</Tooltip>
</TooltipProvider>
{state.isFormVisible && (
<div className="relative">
<form className="max-w-full flex rounded bg-white" onSubmit={handleSubmit(onSubmit)}>
<input
className="rounded-r-none float-left border-0 block w-full h-[calc(1.5em+0.75rem+2px)] p-[0.375rem_0.75rem] text-base font-normal leading-6 text-gray-700 bg-white border border-gray-300 rounded transition duration-150 ease-in-out focus:border-blue-500 focus:outline-none"
{...register('description')}
placeholder="Brief description for this grouping..."
maxLength={98}
/>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<button
className="text-primary border-none bg-white text-text-color px-1 py-0.5 my-0 mx-0.5 h-9"
disabled={isSubmitting}
type="submit"
aria-label="circle-check"
>
<FontAwesomeIcon
className="text-[2rem]"
role="button"
aria-hidden="true"
icon={faCircleCheck}
/>
</button>
</TooltipTrigger>
<TooltipContent className="bg-black text-white max-w-48 text-center border-none">
Save description
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={closeForm}
className="text-primary border-none bg-white text-text-color px-1 py-0.5 my-0 mx-0.5 h-9"
aria-label="times-circle"
>
<FontAwesomeIcon
className="text-[2rem]"
role="button"
aria-hidden="true"
icon={faTimesCircle}
/>
</button>
</TooltipTrigger>
<TooltipContent className="bg-black text-white max-w-48 text-center border-none">
Cancel changes
</TooltipContent>
</Tooltip>
</TooltipProvider>
</form>
{currentDescription.length >= 98 && (
<Alert
data-testid="description-alert"
className="bg-red-100 text-[rgb(114,28,36)] lg:w-max lg:h-[50px] md:w-max md:h-[50px] sm:h-1/2 pt-2 pl-2 pr-2 mb-1 mt-1 border"
>
<strong>Maximum length reached. </strong>A grouping&apos;s description cannot exceed 98
characters.
</Alert>
)}
</div>
)}
</p>
</div>
);
};

export default DescriptionForm;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use client';

import { Button } from '@/components/ui/button';
import { FileInput, ChevronDown } from 'lucide-react';

const ExportDropdown = () => {
return (
<div className="btn-group dropdown float-right">
<Button aria-label="Export Grouping">
<FileInput className="mr-1" />
Export Grouping
<ChevronDown className="ml-1" />
</Button>
</div>
);
};

export default ExportDropdown;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use client';

import DescriptionForm from './description-form';

const GroupingHeader = ({
groupName,
groupDescription,
groupPath
}: {
groupName: string;
groupDescription: string;
groupPath: string;
}) => {
return (
<div className="py-3 px-5 border-b box-border rounded-b-none rounded-t mt-4 mt-0 bg-uh-teal">
<div className="flex flex-row table m-auto w-full p-0">
<div className="md:w-2/3 table-footer-group">
<h2 className="text-gray-100 mb-0 text-[2rem] text-center md:text-left">{groupName}</h2>
</div>
</div>

<div className="flex-row">
<div className="md:w-full">
<p className="text-gray-100 mb-0">
<b>Path:</b> {groupPath}
</p>
</div>
</div>

<div className="flex-row">
<div className="md:w-full">
<DescriptionForm groupDescription={groupDescription} groupPath={groupPath} />
</div>
</div>
</div>
);
};

export default GroupingHeader;
28 changes: 28 additions & 0 deletions ui/src/app/groupings/[groupingPath]/_components/return-buttons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client';

import Link from 'next/link';
import { Button } from '@/components/ui/button';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons';

const ReturnButtons = ({ fromManageSubject }: { fromManageSubject: boolean }) => {
return (
<>
{!fromManageSubject ? (
<Link href="/groupings">
<Button className="btn btn-primary" type="button">
<FontAwesomeIcon className="mr-1" icon={faArrowLeft} /> Return to Groupings List
</Button>
</Link>
) : (
<Link href="/manage-person">
<Button className="btn btn-primary" type="button">
<FontAwesomeIcon className="mr-1" icon={faArrowLeft} /> Return to Manage Person
</Button>
</Link>
)}
</>
);
};

export default ReturnButtons;
Loading

0 comments on commit 3024a2b

Please sign in to comment.