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