From a765b4f7651ce57c6e86b83c51cccd99a2209d5f Mon Sep 17 00:00:00 2001 From: Tin Geber <tin@greenhost.nl> Date: Fri, 1 Dec 2023 00:08:38 +0100 Subject: [PATCH] md5 for gravatar pulls, and new user list UI test --- frontend/package.json | 2 + frontend/src/components/Header/Header.tsx | 8 +- frontend/src/modules/users/Users.tsx | 108 +++++++++++++++++++++- frontend/yarn.lock | 9 +- 4 files changed, 123 insertions(+), 4 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 7485b1ad..2d24ff2a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "@testing-library/user-event": "^12.1.10", "@types/jest": "^26.0.15", "@types/js-yaml": "^4.0.5", + "@types/md5": "^2.3.5", "@types/node": "^18.0.0", "@types/react-dom": "^17.0.2", "axios": "^0.21.1", @@ -24,6 +25,7 @@ "gray-matter": "^4.0.3", "lint-staged": "^11.1.1", "lodash": "^4.17.21", + "md5": "^2.3.0", "prismjs": "^1.24.1", "react": "17.0.2", "react-dom": "^17.0.2", diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx index 019e73d2..6892c442 100644 --- a/frontend/src/components/Header/Header.tsx +++ b/frontend/src/components/Header/Header.tsx @@ -4,6 +4,7 @@ import { MenuIcon, XIcon, ExclamationIcon } from '@heroicons/react/outline'; import { performApiCall } from 'src/services/api'; import { useAuth } from 'src/services/auth'; import { useApps } from 'src/services/apps'; +import md5 from 'md5'; import Gravatar from 'react-gravatar'; import { Link, useLocation } from 'react-router-dom'; import clsx from 'clsx'; @@ -157,7 +158,12 @@ const Header: React.FC<HeaderProps> = () => { <Menu.Button className="bg-white rounded-full flex text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary"> <span className="sr-only">Open user menu</span> <span className="inline-flex items-center justify-center h-8 w-8 rounded-full bg-gray-500 overflow-hidden"> - <Gravatar email={currentUser?.email || undefined} size={32} rating="pg" protocol="https://" /> + <Gravatar + md5={md5(currentUser?.email ?? 'no-user') || undefined} + size={32} + rating="pg" + protocol="https://" + /> </span> </Menu.Button> </div> diff --git a/frontend/src/modules/users/Users.tsx b/frontend/src/modules/users/Users.tsx index b4111122..76ff240c 100644 --- a/frontend/src/modules/users/Users.tsx +++ b/frontend/src/modules/users/Users.tsx @@ -6,7 +6,7 @@ */ // React main -import React, { useState, useCallback, useEffect, useMemo, HTMLProps } from 'react'; +import React, { Fragment, useState, useCallback, useEffect, useMemo, HTMLProps } from 'react'; // Icons import { @@ -21,6 +21,11 @@ import { ChevronRightIcon, } from '@heroicons/react/solid'; import { CogIcon } from '@heroicons/react/outline'; +import { Menu, Transition } from '@headlessui/react'; + +// avatars +import Gravatar from 'react-gravatar'; +import md5 from 'md5'; // API - Redux import { useUsers, User } from 'src/services/users'; @@ -84,6 +89,7 @@ export const Users: React.FC = () => { }; }, []); + users.sort((a, b) => a.name.localeCompare(b.name)); const filterSearch = useMemo(() => { return users.filter( (item: any) => @@ -112,6 +118,11 @@ export const Users: React.FC = () => { // setSelectedRowsIds(rows); // }, []); + // class function for the new user table + function classNames(...classes: string[]) { + return classes.filter(Boolean).join(' '); + } + // //////////////////////// // New Table Start // //////////////////////// @@ -455,6 +466,101 @@ export const Users: React.FC = () => { </div> </div> + <ul className="divide-y divide-gray-100"> + {filterSearch.map((person) => ( + <li key={person.email} className="flex justify-between gap-x-6 py-5 px-4 group"> + <div className="flex min-w-0 gap-x-4 items-center"> + {/* <img className="h-12 w-12 flex-none rounded-full bg-gray-50" src={person.imageUrl} alt="" /> */} + <span className="inline-flex items-center relative justify-center h-8 w-8 rounded-full bg-primary overflow-hidden"> + <span className="absolute z-10"> + <Gravatar + md5={md5(person.email) || undefined} + size={32} + rating="pg" + default="blank" + protocol="https://" + /> + </span> + <span className="absolute z-0 text-primary-800">{person.name[0]}</span> + </span> + <div className="min-w-0 flex-auto"> + <p className="text-sm font-semibold leading-6 text-gray-900"> + {/* <a href={person.href} className="hover:underline"> */} + {person.name} + {/* </a> */} + </p> + <p className="mt-1 flex text-xs leading-5 text-gray-500"> + <a href={`mailto:${person.email}`} className="truncate hover:underline"> + {person.email} + </a> + </p> + </div> + </div> + <div className="flex shrink-0 items-center gap-x-6"> + <div className="hidden sm:flex sm:flex-col sm:items-end"> + <p className="text-sm leading-6 text-gray-900">{person.status}</p> + {person.status ? ( + <p className="mt-1 text-xs leading-5 text-gray-500"> + {person.admin ? <span>Admin</span> : <span>User</span>} + </p> + ) : ( + <div className="mt-1 flex items-center gap-x-1.5"> + <div className="flex-none rounded-full bg-emerald-500/20 p-1"> + <div className="h-1.5 w-1.5 rounded-full bg-emerald-500" /> + </div> + <p className="text-xs leading-5 text-gray-500">Online</p> + </div> + )} + </div> + <Menu as="div" className="relative flex-none"> + <Menu.Button className="-m-2.5 block p-2.5 text-gray-500 hover:text-gray-900"> + <span className="sr-only">Open options</span> + <CogIcon className="h-5 w-5" aria-hidden="true" /> + </Menu.Button> + <Transition + as={Fragment} + enter="transition ease-out duration-100" + enterFrom="transform opacity-0 scale-95" + enterTo="transform opacity-100 scale-100" + leave="transition ease-in duration-75" + leaveFrom="transform opacity-100 scale-100" + leaveTo="transform opacity-0 scale-95" + > + <Menu.Items className="absolute right-0 z-10 mt-2 w-32 origin-top-right rounded-md bg-white py-2 shadow-lg ring-1 ring-gray-900/5 focus:outline-none"> + <Menu.Item> + {({ active }) => ( + <a + href="#" + className={classNames( + active ? 'bg-gray-50' : '', + 'block px-3 py-1 text-sm leading-6 text-gray-900', + )} + > + View profile<span className="sr-only">, {person.name}</span> + </a> + )} + </Menu.Item> + <Menu.Item> + {({ active }) => ( + <a + href="#" + className={classNames( + active ? 'bg-gray-50' : '', + 'block px-3 py-1 text-sm leading-6 text-gray-900', + )} + > + Message<span className="sr-only">, {person.name}</span> + </a> + )} + </Menu.Item> + </Menu.Items> + </Transition> + </Menu> + </div> + </li> + ))} + </ul> + {configureModal && ( <UserModal open={configureModal} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 2614f4c4..54688fd0 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1962,6 +1962,11 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.186.tgz#862e5514dd7bd66ada6c70ee5fce844b06c8ee97" integrity sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw== +"@types/md5@^2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.3.5.tgz#481cef0a896e3a5dcbfc5a8a8b02c05958af48a5" + integrity sha512-/i42wjYNgE6wf0j2bcTX6kuowmdL/6PE4IVitMpm2eYKBUuYCprdcWVK+xEF0gcV6ufMCRhtxmReGfc6hIK7Jw== + "@types/mdast@^3.0.0": version "3.0.10" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" @@ -7890,7 +7895,7 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" -md5@^2.1.0: +md5@^2.1.0, md5@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== @@ -11960,7 +11965,7 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" -"tailwindcss@npm:@tailwindcss/postcss7-compat": +"tailwindcss@npm:@tailwindcss/postcss7-compat@^2.2.17": version "2.2.17" resolved "https://registry.yarnpkg.com/@tailwindcss/postcss7-compat/-/postcss7-compat-2.2.17.tgz#dc78f3880a2af84163150ff426a39e42b9ae8922" integrity sha512-3h2svqQAqYHxRZ1KjsJjZOVTQ04m29LjfrLjXyZZEJuvUuJN+BCIF9GI8vhE1s0plS0mogd6E6YLg6mu4Wv/Vw== -- GitLab