diff --git a/ui/public/cogs.svg b/ui/public/cogs.svg old mode 100755 new mode 100644 diff --git a/ui/public/id-card-solid.svg b/ui/public/id-card-solid.svg new file mode 100644 index 00000000..8abeb2c8 --- /dev/null +++ b/ui/public/id-card-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/public/id-email.svg b/ui/public/id-email.svg old mode 100755 new mode 100644 diff --git a/ui/public/key-solid.svg b/ui/public/key-solid.svg new file mode 100644 index 00000000..7555f7ed --- /dev/null +++ b/ui/public/key-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/public/uh-groupings-text.svg b/ui/public/uh-groupings-text.svg new file mode 100644 index 00000000..f323aed7 --- /dev/null +++ b/ui/public/uh-groupings-text.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/public/user-solid.svg b/ui/public/user-solid.svg new file mode 100644 index 00000000..30cbd7d3 --- /dev/null +++ b/ui/public/user-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/public/watch.svg b/ui/public/watch.svg old mode 100755 new mode 100644 diff --git a/ui/public/wrench-solid.svg b/ui/public/wrench-solid.svg new file mode 100644 index 00000000..280b3bd0 --- /dev/null +++ b/ui/public/wrench-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/app/(index)/_components/AfterLogin.tsx b/ui/src/app/(index)/_components/AfterLogin.tsx new file mode 100644 index 00000000..f2851a75 --- /dev/null +++ b/ui/src/app/(index)/_components/AfterLogin.tsx @@ -0,0 +1,126 @@ +import Role from '@/access/Role'; +import Image from 'next/image'; +import { KeyRound } from 'lucide-react'; +import Link from 'next/link'; +import { Button } from '@/components/ui/button'; +import { getNumberOfGroupings, getNumberOfMemberships } from '@/services/GroupingsApiService'; +import { getCurrentUser } from '@/access/AuthenticationService'; + +const AfterLogin = async ()=>{ + const [currentUser, numberOfGroupings, numberOfMemberships] = await Promise.all([ + getCurrentUser(), + getNumberOfGroupings(), + getNumberOfMemberships() + ]); + + const getHighestRole = () => { + if (currentUser.roles.includes(Role.ADMIN)) return 'Admin'; + else if (currentUser.roles.includes(Role.OWNER)) return 'Owner'; + else return 'Member'; + }; + + const pageInfoItems = [ + { + title: 'Admin', + description: 'Manage the list of Administrators for this service.' + + ' Search for and manage any grouping on behalf of the owner.', + href: '/admin', + icon: { + alt: 'key-solid', + src: 'uhgroupings/key-solid.svg', + width: 48, + height: 48, + }, + role: Role.ADMIN + }, + { + title: 'Memberships', + description: 'View and manage my memberships. Search for new groupings to join as a member.', + href: '/memberships', + icon: { + src: 'uhgroupings/id-card-solid.svg', + alt: 'id-card', + width: 54, + height: 48, + }, + number: numberOfMemberships, + role: Role.UH + }, + { + title: 'Groupings', + description: 'Review members, manage Include and Exclude lists, ' + + 'configure preferences, and export members.', + href: '/groupings', + icon: { + alt: 'wrench-solid', + src: 'uhgroupings/wrench-solid.svg', + width: 48, + height: 48 + }, + number: numberOfGroupings, + role: Role.OWNER + } + ]; + + return ( +
+
+
+
+
+
+ user-solid +
+ +
+
+
+
+

Welcome, {currentUser.firstName}!

+

Role: {getHighestRole()}

+
+
+
+
+ +
+ {pageInfoItems + .filter((pageInfoItem) => currentUser.roles.includes(pageInfoItem.role)) + .map((pageInfoItem, index) => ( +
+
+
+ {pageInfoItem.icon.alt} + {pageInfoItem.number + && {pageInfoItem.number}} +
+

{pageInfoItem.title}

+

{pageInfoItem.description}

+
+ + + +
+ ))} +
+
+ ); +}; + +export default AfterLogin; diff --git a/ui/src/app/(index)/_components/Announcements.tsx b/ui/src/app/(index)/_components/Announcements.tsx new file mode 100644 index 00000000..ee796ea8 --- /dev/null +++ b/ui/src/app/(index)/_components/Announcements.tsx @@ -0,0 +1,29 @@ +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; +import { AlertCircle } from 'lucide-react'; +import { getAnnouncements } from '@/services/GroupingsApiService'; + +const Announcements = async () => { + const announcements = await getAnnouncements(); + const activeAnnouncements = () => { + if (!announcements || !announcements.announcements) { + return [] + } + return announcements.announcements.filter((announcement) => announcement.state === 'Active') + .map((announcement) => announcement.message) + }; + + return ( +
+ {activeAnnouncements().map((announcement:string, index:number) => ( + + + Announcement + + {announcement} + + ))} +
+ ); +}; + +export default Announcements; diff --git a/ui/src/app/(index)/_components/BeforeLogin.tsx b/ui/src/app/(index)/_components/BeforeLogin.tsx new file mode 100644 index 00000000..2a1c0ef1 --- /dev/null +++ b/ui/src/app/(index)/_components/BeforeLogin.tsx @@ -0,0 +1,20 @@ +import { Button } from '@/components/ui/button'; +import { ArrowRight } from 'lucide-react'; +import UHGroupingsInfo from '@/components/UHGroupingsInfo'; + +const BeforeLogin = () => ( +
+ +
+
+ + + +
+
+
+); + +export default BeforeLogin; diff --git a/ui/src/app/(index)/_components/LoginButton.tsx b/ui/src/app/(index)/_components/LoginButton.tsx new file mode 100644 index 00000000..544f01c9 --- /dev/null +++ b/ui/src/app/(index)/_components/LoginButton.tsx @@ -0,0 +1,31 @@ +'use client'; +import { Button } from '@/components/ui/button'; +import Role from '@/access/Role'; +import User from '@/access/User'; +import { login, logout } from '@/access/AuthenticationService'; + +const LoginButton = ({ + currentUser +}: { + currentUser: User; +}) => ( + <> + {!currentUser.roles.includes(Role.UH) ? ( + + ) : ( + + )} + +); + +export default LoginButton; diff --git a/ui/src/app/(index)/page.tsx b/ui/src/app/(index)/page.tsx index b0223723..ff6bde38 100644 --- a/ui/src/app/(index)/page.tsx +++ b/ui/src/app/(index)/page.tsx @@ -1,6 +1,54 @@ -const Home = () => { +import Image from 'next/image'; +import BeforeLogin from '@/app/(index)/_components/BeforeLogin'; +import AfterLogin from '@/app/(index)/_components/AfterLogin'; +import { getCurrentUser } from '@/access/AuthenticationService'; +import Role from '@/access/Role'; +import LoginButton from '@/app/(index)/_components/LoginButton'; +import Announcements from '@/app/(index)/_components/Announcements'; + +const Home = async () => { + const currentUser = await getCurrentUser(); + return ( - null +
+
+ +
+
+
+

UH Groupings

+ UH Groupings logotype + +

Manage your groupings in one place, use them in many.

+
+ {!currentUser.roles.includes(Role.UH) && + } +
+
+
+
+ UH Groupings +
+
+
+ + {currentUser.roles.includes(Role.UH) ? ( + + ) : ( + + )} +
); } diff --git a/ui/src/app/about/page.tsx b/ui/src/app/about/page.tsx index c8d17f53..92d02ce0 100644 --- a/ui/src/app/about/page.tsx +++ b/ui/src/app/about/page.tsx @@ -1,58 +1,8 @@ -import Image from 'next/image'; +import UHGroupingsInfo from '@/components/UHGroupingsInfo'; const About = () => (
-
-
-
-

What is a UH Grouping?

-

A grouping is a collection of members - (e.g., all full-time - Hilo faculty).

-
-
-
-
- Cogs icon -
-

Create groupings, manage grouping memberships, - control members' self-service - options, designate sync destinations, and more.

-
- -
-
- Email icon -
-

Synchronize groupings email LISTSERV lists, attributes for access - control via - CAS and LDAP, etc.

-
- -
-
- Watch icon -
-

Leverage group data from official sources, which can - substantially reduce the - manual overhead of membership management.

-
-
-
-
- +

GENERAL INFO

@@ -60,8 +10,10 @@ const About = () => (

How do I request a new grouping?

A request form is available. + href={'https://uhawaii.atlassian.net/wiki/spaces/UHIAM/' + + 'pages/13402308/UH+Groupings+Request+Form'} + aria-label="A request form is available">A request form is available + .

Exactly what is a grouping?

diff --git a/ui/src/components/UHGroupingsInfo.tsx b/ui/src/components/UHGroupingsInfo.tsx new file mode 100644 index 00000000..c454ef8d --- /dev/null +++ b/ui/src/components/UHGroupingsInfo.tsx @@ -0,0 +1,65 @@ +import Image from 'next/image'; + +const UHGroupingsInfo = ({ + size +}: { + size?: 'lg' | 'default'; +}) => { + const color = size === 'lg' ? 'text-text-color' : 'text-uh-black'; + const textSize = size === 'lg' ? 'text-[1.2rem]' : 'text-base'; + + const infoItems = [ + { + description: 'Create groupings, manage grouping memberships, control members\' self-service options, ' + + 'designate sync destinations, and more.', + icon: { + src: '/uhgroupings/cogs.svg', + alt: 'Cogs icon' + } + }, + { + description: 'Synchronize groupings email LISTSERV lists, ' + + 'attributes for access control via CAS and LDAP, etc..', + icon: { + src: '/uhgroupings/id-email.svg', + alt: 'Email icon' + } + }, + { + description: 'Leverage group data from official sources, ' + + 'which can substantially reduce the manual overhead of membership management.', + icon: { + src: '/uhgroupings/watch.svg', + alt: 'Watch icon' + } + } + ]; + + return ( +
+
+
+
+

What is a UH Grouping?

+

A grouping is a collection of members + (e.g., all full-time + Hilo faculty).

+
+
+ {infoItems.map((infoItem, index) => ( +
+
+ {infoItem.icon.alt} +
+

{infoItem.description}

+
+ ))} +
+
+
+
+ ); +}; + +export default UHGroupingsInfo; diff --git a/ui/src/components/layout/navbar/Navbar.tsx b/ui/src/components/layout/navbar/Navbar.tsx index a9a5921c..12da83ed 100644 --- a/ui/src/components/layout/navbar/Navbar.tsx +++ b/ui/src/components/layout/navbar/Navbar.tsx @@ -9,11 +9,10 @@ import TimeoutModal from '@/components/modal/TimeoutModal'; const Navbar = async () => { const currentUser = await getCurrentUser(); - - return ( + return ( <> -