Added a badge with the amount of pending tickets assigned to the user

This commit is contained in:
Joao Ramos
2024-02-13 17:29:55 +00:00
parent 64b1d9266e
commit b63ba3f316
4 changed files with 47 additions and 5 deletions

View File

@@ -34,6 +34,7 @@ export default function Layout({user, children, className, navDisabled = false,
onFocusLayerMouseEnter={onFocusLayerMouseEnter} onFocusLayerMouseEnter={onFocusLayerMouseEnter}
className="-md:hidden" className="-md:hidden"
userType={user.type} userType={user.type}
userId={user.id}
/> />
<div <div
className={clsx( className={clsx(

View File

@@ -24,6 +24,7 @@ import { preventNavigation } from "@/utils/navigation.disabled";
import { useState } from "react"; import { useState } from "react";
import usePreferencesStore from "@/stores/preferencesStore"; import usePreferencesStore from "@/stores/preferencesStore";
import { Type } from "@/interfaces/user"; import { Type } from "@/interfaces/user";
import useTicketsListener from '@/hooks/useTicketsListener';
interface Props { interface Props {
path: string; path: string;
navDisabled?: boolean; navDisabled?: boolean;
@@ -31,6 +32,7 @@ interface Props {
onFocusLayerMouseEnter?: () => void; onFocusLayerMouseEnter?: () => void;
className?: string; className?: string;
userType?: Type; userType?: Type;
userId?: string;
} }
interface NavProps { interface NavProps {
@@ -40,6 +42,7 @@ interface NavProps {
keyPath: string; keyPath: string;
disabled?: boolean; disabled?: boolean;
isMinimized?: boolean; isMinimized?: boolean;
badge?: number;
} }
const Nav = ({ const Nav = ({
@@ -49,7 +52,10 @@ const Nav = ({
keyPath, keyPath,
disabled = false, disabled = false,
isMinimized = false, isMinimized = false,
}: NavProps) => ( badge,
}: NavProps) => {
const displayBadge = badge && badge > 0 ? true : false;
return (
<Link <Link
href={!disabled ? keyPath : ""} href={!disabled ? keyPath : ""}
className={clsx( className={clsx(
@@ -64,8 +70,10 @@ const Nav = ({
> >
<Icon size={24} /> <Icon size={24} />
{!isMinimized && <span className="text-lg font-semibold">{label}</span>} {!isMinimized && <span className="text-lg font-semibold">{label}</span>}
{displayBadge && <div className="badge badge-primary badge-sm">{badge}</div>}
</Link> </Link>
); );
}
export default function Sidebar({ export default function Sidebar({
path, path,
@@ -74,6 +82,7 @@ export default function Sidebar({
userType, userType,
onFocusLayerMouseEnter, onFocusLayerMouseEnter,
className, className,
userId,
}: Props) { }: Props) {
const router = useRouter(); const router = useRouter();
@@ -82,6 +91,8 @@ export default function Sidebar({
state.toggleSidebarMinimized, state.toggleSidebarMinimized,
]); ]);
const {totalAssignedTickets } = useTicketsListener(userId);
const logout = async () => { const logout = async () => {
axios.post("/api/logout").finally(() => { axios.post("/api/logout").finally(() => {
setTimeout(() => router.reload(), 500); setTimeout(() => router.reload(), 500);
@@ -177,6 +188,7 @@ export default function Sidebar({
path={path} path={path}
keyPath="/tickets" keyPath="/tickets"
isMinimized={isMinimized} isMinimized={isMinimized}
badge={totalAssignedTickets}
/> />
)} )}
{userType === "developer" && ( {userType === "developer" && (

View File

@@ -1,22 +1,22 @@
import { Ticket } from "@/interfaces/ticket"; import { Ticket } from "@/interfaces/ticket";
import { Code, Group, User } from "@/interfaces/user"; import { Code, Group, User } from "@/interfaces/user";
import axios from "axios"; import axios from "axios";
import { useEffect, useState } from "react"; import { useEffect, useState, useCallback } from "react";
export default function useTickets() { export default function useTickets() {
const [tickets, setTickets] = useState<Ticket[]>([]); const [tickets, setTickets] = useState<Ticket[]>([]);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false); const [isError, setIsError] = useState(false);
const getData = () => { const getData = useCallback(() => {
setIsLoading(true); setIsLoading(true);
axios axios
.get<Ticket[]>(`/api/tickets`) .get<Ticket[]>(`/api/tickets`)
.then((response) => setTickets(response.data)) .then((response) => setTickets(response.data))
.finally(() => setIsLoading(false)); .finally(() => setIsLoading(false));
}; }, []);
useEffect(getData, []); useEffect(getData, [getData]);
return { tickets, isLoading, isError, reload: getData }; return { tickets, isLoading, isError, reload: getData };
} }

View File

@@ -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;