From b63ba3f31640c55abf0a03528d0c8513fedccd5c Mon Sep 17 00:00:00 2001 From: Joao Ramos Date: Tue, 13 Feb 2024 17:29:55 +0000 Subject: [PATCH 1/2] Added a badge with the amount of pending tickets assigned to the user --- src/components/High/Layout.tsx | 1 + src/components/Sidebar.tsx | 14 +++++++++++++- src/hooks/useTickets.tsx | 8 ++++---- src/hooks/useTicketsListener.tsx | 29 +++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 src/hooks/useTicketsListener.tsx diff --git a/src/components/High/Layout.tsx b/src/components/High/Layout.tsx index 9feadad1..79eb2d2a 100644 --- a/src/components/High/Layout.tsx +++ b/src/components/High/Layout.tsx @@ -34,6 +34,7 @@ export default function Layout({user, children, className, navDisabled = false, onFocusLayerMouseEnter={onFocusLayerMouseEnter} className="-md:hidden" userType={user.type} + userId={user.id} />
void; className?: string; userType?: Type; + userId?: string; } interface NavProps { @@ -40,6 +42,7 @@ interface NavProps { keyPath: string; disabled?: boolean; isMinimized?: boolean; + badge?: number; } const Nav = ({ @@ -49,7 +52,10 @@ const Nav = ({ keyPath, disabled = false, isMinimized = false, -}: NavProps) => ( + badge, +}: NavProps) => { + const displayBadge = badge && badge > 0 ? true : false; + return ( {!isMinimized && {label}} + {displayBadge &&
{badge}
} ); + } export default function Sidebar({ path, @@ -74,6 +82,7 @@ export default function Sidebar({ userType, onFocusLayerMouseEnter, className, + userId, }: Props) { const router = useRouter(); @@ -82,6 +91,8 @@ export default function Sidebar({ state.toggleSidebarMinimized, ]); + const {totalAssignedTickets } = useTicketsListener(userId); + const logout = async () => { axios.post("/api/logout").finally(() => { setTimeout(() => router.reload(), 500); @@ -177,6 +188,7 @@ export default function Sidebar({ path={path} keyPath="/tickets" isMinimized={isMinimized} + badge={totalAssignedTickets} /> )} {userType === "developer" && ( diff --git a/src/hooks/useTickets.tsx b/src/hooks/useTickets.tsx index c90fd639..9a5ba240 100644 --- a/src/hooks/useTickets.tsx +++ b/src/hooks/useTickets.tsx @@ -1,22 +1,22 @@ import { Ticket } from "@/interfaces/ticket"; import { Code, Group, User } from "@/interfaces/user"; import axios from "axios"; -import { useEffect, useState } from "react"; +import { useEffect, useState, useCallback } from "react"; export default function useTickets() { const [tickets, setTickets] = useState([]); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); - const getData = () => { + const getData = useCallback(() => { setIsLoading(true); axios .get(`/api/tickets`) .then((response) => setTickets(response.data)) .finally(() => setIsLoading(false)); - }; + }, []); - useEffect(getData, []); + useEffect(getData, [getData]); return { tickets, isLoading, isError, reload: getData }; } diff --git a/src/hooks/useTicketsListener.tsx b/src/hooks/useTicketsListener.tsx new file mode 100644 index 00000000..520ee947 --- /dev/null +++ b/src/hooks/useTicketsListener.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import useTickets from "./useTickets"; + +const useTicketsListener = (userId?: string) => { + const { tickets, reload } = useTickets(); + + React.useEffect(() => { + const intervalId = setInterval(() => { + reload(); + }, 60 * 1000); + + return () => clearInterval(intervalId); + }, [reload]); + + if (userId) { + const assignedTickets = tickets.filter( + (ticket) => ticket.assignedTo === userId && ticket.status === "submitted" + ); + + return { + assignedTickets, + totalAssignedTickets: assignedTickets.length, + }; + } + + return {}; +}; + +export default useTicketsListener; From d87de9fea96542a369fc813f4c5dff653203ea62 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Wed, 14 Feb 2024 00:05:08 +0000 Subject: [PATCH 2/2] Updated the styling a little bit --- src/components/Sidebar.tsx | 471 +++++++++++++++---------------------- 1 file changed, 185 insertions(+), 286 deletions(-) diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 6260648c..041aa7a9 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,307 +1,206 @@ import clsx from "clsx"; -import { IconType } from "react-icons"; -import { MdSpaceDashboard } from "react-icons/md"; +import {IconType} from "react-icons"; +import {MdSpaceDashboard} from "react-icons/md"; import { - BsFileEarmarkText, - BsClockHistory, - BsPencil, - BsGraphUp, - BsChevronBarRight, - BsChevronBarLeft, - BsShieldFill, - BsCloudFill, - BsCurrencyDollar, - BsClipboardData, + BsFileEarmarkText, + BsClockHistory, + BsPencil, + BsGraphUp, + BsChevronBarRight, + BsChevronBarLeft, + BsShieldFill, + BsCloudFill, + BsCurrencyDollar, + BsClipboardData, } from "react-icons/bs"; -import { RiLogoutBoxFill } from "react-icons/ri"; -import { SlPencil } from "react-icons/sl"; -import { FaAward } from "react-icons/fa"; +import {RiLogoutBoxFill} from "react-icons/ri"; +import {SlPencil} from "react-icons/sl"; +import {FaAward} from "react-icons/fa"; import Link from "next/link"; -import { useRouter } from "next/router"; +import {useRouter} from "next/router"; import axios from "axios"; import FocusLayer from "@/components/FocusLayer"; -import { preventNavigation } from "@/utils/navigation.disabled"; -import { useState } from "react"; +import {preventNavigation} from "@/utils/navigation.disabled"; +import {useEffect, useState} from "react"; import usePreferencesStore from "@/stores/preferencesStore"; -import { Type } from "@/interfaces/user"; -import useTicketsListener from '@/hooks/useTicketsListener'; +import {Type} from "@/interfaces/user"; +import useTicketsListener from "@/hooks/useTicketsListener"; interface Props { - path: string; - navDisabled?: boolean; - focusMode?: boolean; - onFocusLayerMouseEnter?: () => void; - className?: string; - userType?: Type; - userId?: string; + path: string; + navDisabled?: boolean; + focusMode?: boolean; + onFocusLayerMouseEnter?: () => void; + className?: string; + userType?: Type; + userId?: string; } interface NavProps { - Icon: IconType; - label: string; - path: string; - keyPath: string; - disabled?: boolean; - isMinimized?: boolean; - badge?: number; + Icon: IconType; + label: string; + path: string; + keyPath: string; + disabled?: boolean; + isMinimized?: boolean; + badge?: number; } -const Nav = ({ - Icon, - label, - path, - keyPath, - disabled = false, - isMinimized = false, - badge, -}: NavProps) => { - const displayBadge = badge && badge > 0 ? true : false; - return ( - - - {!isMinimized && {label}} - {displayBadge &&
{badge}
} - -); - } +const Nav = ({Icon, label, path, keyPath, disabled = false, isMinimized = false, badge}: NavProps) => { + return ( + + + {!isMinimized && {label}} + {!!badge && badge > 0 && ( +
+ {badge} +
+ )} + + ); +}; -export default function Sidebar({ - path, - navDisabled = false, - focusMode = false, - userType, - onFocusLayerMouseEnter, - className, - userId, -}: Props) { - const router = useRouter(); +export default function Sidebar({path, navDisabled = false, focusMode = false, userType, onFocusLayerMouseEnter, className, userId}: Props) { + const router = useRouter(); - const [isMinimized, toggleMinimize] = usePreferencesStore((state) => [ - state.isSidebarMinimized, - state.toggleSidebarMinimized, - ]); + const [isMinimized, toggleMinimize] = usePreferencesStore((state) => [state.isSidebarMinimized, state.toggleSidebarMinimized]); - const {totalAssignedTickets } = useTicketsListener(userId); + const {totalAssignedTickets} = useTicketsListener(userId); - const logout = async () => { - axios.post("/api/logout").finally(() => { - setTimeout(() => router.reload(), 500); - }); - }; + useEffect(() => console.log(totalAssignedTickets), [totalAssignedTickets]); - const disableNavigation = preventNavigation(navDisabled, focusMode); + const logout = async () => { + axios.post("/api/logout").finally(() => { + setTimeout(() => router.reload(), 500); + }); + }; - return ( -
-
-
-
-
+ const disableNavigation = preventNavigation(navDisabled, focusMode); -
-
- {isMinimized ? ( - - ) : ( - - )} - {!isMinimized && ( - Minimize - )} -
-
{} : logout} - className={clsx( - "hover:text-mti-rose flex cursor-pointer items-center gap-4 rounded-full p-4 text-black transition duration-300 ease-in-out", - isMinimized ? "w-fit" : "w-full min-w-[250px] px-8", - )} - > - - {!isMinimized && ( - Log Out - )} -
-
- {focusMode && ( - - )} -
- ); + return ( +
+
+
+
+
+ +
+
+ {isMinimized ? : } + {!isMinimized && Minimize} +
+
{} : logout} + className={clsx( + "hover:text-mti-rose flex cursor-pointer items-center gap-4 rounded-full p-4 text-black transition duration-300 ease-in-out", + isMinimized ? "w-fit" : "w-full min-w-[250px] px-8", + )}> + + {!isMinimized && Log Out} +
+
+ {focusMode && } +
+ ); }