diff --git a/app/client/modules/repositories/routes/repositories.tsx b/app/client/modules/repositories/routes/repositories.tsx index c7f65534..1288fee7 100644 --- a/app/client/modules/repositories/routes/repositories.tsx +++ b/app/client/modules/repositories/routes/repositories.tsx @@ -1,5 +1,5 @@ import { useSuspenseQuery } from "@tanstack/react-query"; -import { Database, Plus, RotateCcw } from "lucide-react"; +import { ArrowDown, ArrowUp, ArrowUpDown, Database, Plus, RotateCcw } from "lucide-react"; import { useState } from "react"; import { listRepositoriesOptions } from "~/client/api-client/@tanstack/react-query.gen"; import { RepositoryIcon } from "~/client/components/repository-icon"; @@ -12,11 +12,38 @@ import { cn } from "~/client/lib/utils"; import { StatusDot } from "~/client/components/status-dot"; import { EmptyState } from "~/client/components/empty-state"; import { useNavigate } from "@tanstack/react-router"; +import type { RepositoryBackend } from "@zerobyte/core/restic"; + +type SortColumn = "name" | "backend" | "status" | "compression"; +type SortDirection = "asc" | "desc"; +type RepositoryRow = { + id: string; + shortId: string; + name: string; + type: RepositoryBackend; + status: string | null; + compressionMode?: string | null; +}; + +const getSortValue = (column: SortColumn, repository: RepositoryRow) => { + switch (column) { + case "name": + return repository.name; + case "backend": + return repository.type; + case "status": + return repository.status || ""; + case "compression": + return repository.compressionMode || ""; + } +}; export function RepositoriesPage() { const [searchQuery, setSearchQuery] = useState(""); const [statusFilter, setStatusFilter] = useState(""); const [backendFilter, setBackendFilter] = useState(""); + const [sortColumn, setSortColumn] = useState("name"); + const [sortDirection, setSortDirection] = useState("asc"); const clearFilters = () => { setSearchQuery(""); @@ -30,15 +57,47 @@ export function RepositoriesPage() { ...listRepositoriesOptions(), }); - const filteredRepositories = data.filter((repository) => { + const repositories = data as RepositoryRow[]; + + const toggleSort = (column: SortColumn) => { + if (sortColumn === column) { + setSortDirection((prev) => (prev === "asc" ? "desc" : "asc")); + return; + } + + setSortColumn(column); + setSortDirection("asc"); + }; + + const renderSortIcon = (column: SortColumn) => { + if (sortColumn !== column) { + return ; + } + + return sortDirection === "asc" ? ( + + ) : ( + + ); + }; + + const filteredRepositories = repositories.filter((repository) => { const matchesSearch = repository.name.toLowerCase().includes(searchQuery.toLowerCase()); const matchesStatus = !statusFilter || repository.status === statusFilter; const matchesBackend = !backendFilter || repository.type === backendFilter; return matchesSearch && matchesStatus && matchesBackend; }); - const hasNoRepositories = data.length === 0; - const hasNoFilteredRepositories = filteredRepositories.length === 0 && !hasNoRepositories; + const sortedFilteredRepositories = [...filteredRepositories].sort((a, b) => { + const valueA = getSortValue(sortColumn, a).toLowerCase(); + const valueB = getSortValue(sortColumn, b).toLowerCase(); + const result = valueA.localeCompare(valueB); + + return sortDirection === "asc" ? result : -result; + }); + + const hasNoRepositories = repositories.length === 0; + const hasNoFilteredRepositories = sortedFilteredRepositories.length === 0 && !hasNoRepositories; if (hasNoRepositories) { return ( @@ -103,10 +162,54 @@ export function RepositoriesPage() { - Name - Backend - Compression - Status + + + + + + + + + + + + @@ -121,7 +224,7 @@ export function RepositoriesPage() { - {filteredRepositories.map((repository) => ( + {sortedFilteredRepositories.map((repository) => ( - {filteredRepositories.length} repositor - {filteredRepositories.length === 1 ? "y" : "ies"} + {sortedFilteredRepositories.length} repositor + {sortedFilteredRepositories.length === 1 ? "y" : "ies"} )}