From 01a5689ddeefb95e39272174d45eb62a79b1549d Mon Sep 17 00:00:00 2001 From: Hai Phuc Nguyen Date: Sun, 1 Dec 2024 23:41:42 -0800 Subject: [PATCH] Minor Ui improvement (#24) --- src/app/portal/users/page.tsx | 2 +- src/components/teams/team-list.tsx | 199 ++++++++++-------- src/components/teams/team-requests.tsx | 81 +++---- .../users/{users-list.tsx => user-list.tsx} | 0 4 files changed, 145 insertions(+), 137 deletions(-) rename src/components/users/{users-list.tsx => user-list.tsx} (100%) diff --git a/src/app/portal/users/page.tsx b/src/app/portal/users/page.tsx index 04031dd..17f3ebe 100644 --- a/src/app/portal/users/page.tsx +++ b/src/app/portal/users/page.tsx @@ -1,5 +1,5 @@ import { SimpleContentView } from "@/components/admin-panel/simple-content-view"; -import { UserList } from "@/components/users/users-list"; +import { UserList } from "@/components/users/user-list"; const breadcrumbItems = [ { title: "Dashboard", link: "/portal" }, diff --git a/src/components/teams/team-list.tsx b/src/components/teams/team-list.tsx index 88b2537..1477e73 100644 --- a/src/components/teams/team-list.tsx +++ b/src/components/teams/team-list.tsx @@ -1,12 +1,20 @@ "use client"; -import { Ellipsis, Pencil, Plus, Trash } from "lucide-react"; +import { + ArrowDownAZ, + ArrowUpAZ, + Ellipsis, + Pencil, + Plus, + Trash, +} from "lucide-react"; import Link from "next/link"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import React, { useEffect, useState } from "react"; import { Heading } from "@/components/heading"; import { EntitiesDeleteDialog } from "@/components/shared/entity-delete-dialog"; +import LoadingPlaceHolder from "@/components/shared/loading-place-holder"; import PaginationExt from "@/components/shared/pagination-ext"; import DefaultTeamLogo from "@/components/teams/team-logo"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; @@ -36,14 +44,15 @@ import { TeamDTO } from "@/types/teams"; export const TeamList = () => { const router = useRouter(); - const [items, setItems] = useState>([]); // Store the items + const [items, setItems] = useState>([]); const [teamSearchTerm, setTeamSearchTerm] = useState( undefined, ); - const [currentPage, setCurrentPage] = useState(1); // Track current page - const [totalPages, setTotalPages] = useState(0); // Total pages + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(0); const [totalElements, setTotalElements] = useState(0); - const [loading, setLoading] = useState(false); // Loading state + const [loading, setLoading] = useState(false); + const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc"); const [isDialogOpen, setDialogOpen] = useState(false); const [selectedTeam, setSelectedTeam] = useState(null); @@ -69,10 +78,15 @@ export const TeamList = () => { : [], }; - // Fetch data using the QueryDTO const pageResult = await searchTeams(query, { page: currentPage, size: 10, + sort: [ + { + field: "name", + direction: sortDirection, + }, + ], }); setItems(pageResult.content); setTotalElements(pageResult.totalElements); @@ -93,11 +107,13 @@ export const TeamList = () => { replace(`${pathname}?${params.toString()}`); }, 2000); + const toggleSortDirection = () => { + setSortDirection((prev) => (prev === "asc" ? "desc" : "asc")); + }; + useEffect(() => { fetchData(); - }, [teamSearchTerm, currentPage]); - - if (loading) return
Loading...
; + }, [teamSearchTerm, currentPage, sortDirection]); const showDeleteTeamConfirmationDialog = (team: TeamDTO) => { setSelectedTeam(team); @@ -111,13 +127,11 @@ export const TeamList = () => { return (
-
-
- -
+
+
{ }} defaultValue={searchParams.get("name")?.toString()} /> + {PermissionUtils.canWrite(permissionLevel) && ( {
-
- {items?.map((team) => ( -
-
- - - - - + +
+ ) : ( + <> +
+ {items?.map((team) => ( +
+
+ + + + + + + + + + + {team.slogan} + + +
+
+ +
{team.description}
+
+ {PermissionUtils.canWrite(permissionLevel) && ( + + + + + + + router.push( + `/portal/teams/${obfuscate(team.id)}/edit`, + ) } - alt="@flexwork" - /> - - - - - - {team.slogan} - - -
-
- -
{team.description}
-
- {PermissionUtils.canWrite(permissionLevel) && ( - - - - - - - router.push(`/portal/teams/${obfuscate(team.id)}/edit`) - } - > - - Edit - - {PermissionUtils.canAccess(permissionLevel) && ( - showDeleteTeamConfirmationDialog(team)} - > - Delete - - )} - - - )} + > + + Edit + + {PermissionUtils.canAccess(permissionLevel) && ( + showDeleteTeamConfirmationDialog(team)} + > + Delete + + )} + + + )} +
+ ))}
- ))} -
- { - setCurrentPage(page); - }} - /> + { + setCurrentPage(page); + }} + /> + + )} {isDialogOpen && selectedTeam && ( ) => { const [open, setOpen] = useState(false); const [searchText, setSearchText] = useState(""); - const [debouncedSearchText, setDebouncedSearchText] = useState(""); // Debounced text + const [debouncedSearchText, setDebouncedSearchText] = useState(""); const [isAscending, setIsAscending] = useState(false); const [workflows, setWorkflows] = useState([]); const [selectedWorkflow, setSelectedWorkflow] = useState( @@ -93,7 +94,6 @@ const TeamRequestsView = ({ entity: team }: ViewProps) => { const groups: GroupFilter[] = []; let assignedGroupFilter: GroupFilter | undefined = undefined; - // Status filters group const statusFilters: Filter[] = []; if (statuses.includes("New")) { statusFilters.push({ @@ -111,52 +111,36 @@ const TeamRequestsView = ({ entity: team }: ViewProps) => { } if (statuses.includes("Assigned")) { assignedGroupFilter = { - logicalOperator: "AND", // Logical "AND" for the two conditions + logicalOperator: "AND", filters: [ - { - field: "isCompleted", - operator: "eq", - value: false, - }, - { - field: "isNew", - operator: "eq", - value: false, - }, + { field: "isCompleted", operator: "eq", value: false }, + { field: "isNew", operator: "eq", value: false }, ], }; } - // If there are any status filters, add them as an OR group if (statusFilters.length > 0 || assignedGroupFilter) { groups.push({ filters: statusFilters, groups: assignedGroupFilter ? [assignedGroupFilter] : [], - logicalOperator: "OR", // Logical "OR" for statuses + logicalOperator: "OR", }); } - // Search text filter if (debouncedSearchText.trim() !== "") { groups.push({ filters: [ { field: "requestTitle", - operator: "lk", // 'lk' for 'like' + operator: "lk", value: `%${debouncedSearchText}%`, }, ], - logicalOperator: "AND", // Logical "AND" with other filters + logicalOperator: "AND", }); } - // Create the final query with groups - const query: QueryDTO = { - groups, - }; - - // Update the query and pagination sort - setQuery(query); + setQuery({ groups }); setPagination((prev) => ({ ...prev, @@ -169,13 +153,12 @@ const TeamRequestsView = ({ entity: team }: ViewProps) => { })); }, [debouncedSearchText, statuses, isAscending]); - // Debounce logic to delay updates to `debouncedSearchText` useEffect(() => { const handler = setTimeout(() => { - setDebouncedSearchText(searchText); // Update the debounced text after 3 seconds + setDebouncedSearchText(searchText); }, 3000); - return () => clearTimeout(handler); // Cleanup the timeout if the input changes again + return () => clearTimeout(handler); }, [searchText]); useEffect(() => { @@ -187,28 +170,24 @@ const TeamRequestsView = ({ entity: team }: ViewProps) => { const fetchTickets = async () => { setLoading(true); + try { - // Construct a new QueryDTO const combinedQuery: QueryDTO = { groups: [ { logicalOperator: "AND", - filters: [ - { field: "team.id", operator: "eq", value: team.id! }, // Add team filter - ], - groups: query.groups || [], // Include existing query groups + filters: [{ field: "team.id", operator: "eq", value: team.id! }], + groups: query.groups || [], }, ], }; - // Pass QueryDTO to searchTeamRequests const pageResult = await searchTeamRequests(combinedQuery, { page: currentPage, size: 10, sort: pagination.sort, }); - // Update state with the results setRequests(pageResult.content); setTotalElements(pageResult.totalElements); setTotalPages(pageResult.totalPages); @@ -221,8 +200,6 @@ const TeamRequestsView = ({ entity: team }: ViewProps) => { fetchTickets(); }, [currentPage, query]); - if (loading) return
Loading...
; - const onCreatedTeamRequestSuccess = () => { fetchTickets(); }; @@ -270,6 +247,7 @@ const TeamRequestsView = ({ entity: team }: ViewProps) => { {workflows.map((workflow) => ( { setSelectedWorkflow(workflow); @@ -298,7 +276,6 @@ const TeamRequestsView = ({ entity: team }: ViewProps) => { )}
- {/* Search Input */} ) => { onChange={(e) => setSearchText(e.target.value)} className="w-full border border-gray-300 dark:border-gray-700" /> - - {/* Toggle Ascending/Descending */} ) => {

- - {/* Status Filter */}
{[ { label: "New", icon: Clock }, @@ -355,14 +328,22 @@ const TeamRequestsView = ({ entity: team }: ViewProps) => {
- - { - setCurrentPage(page); - }} - /> + {loading ? ( +
+ +
+ ) : ( + <> + + { + setCurrentPage(page); + }} + /> + + )}
); }; diff --git a/src/components/users/users-list.tsx b/src/components/users/user-list.tsx similarity index 100% rename from src/components/users/users-list.tsx rename to src/components/users/user-list.tsx