mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 13:22:08 +00:00
Type fixes and PIT history pagination
This commit is contained in:
@@ -68,13 +68,29 @@ const fetchFolderCommitsCount = async ({
|
||||
const fetchFolderCommitHistory = async (
|
||||
workspaceId: string,
|
||||
environment: string,
|
||||
directory: string
|
||||
): Promise<CommitHistoryItem[]> => {
|
||||
const res = await apiRequest.get<CommitHistoryItem[]>("/api/v1/pit/commits", {
|
||||
directory: string,
|
||||
offset: number = 0,
|
||||
limit: number = 20,
|
||||
search?: string,
|
||||
sort: "asc" | "desc" = "desc"
|
||||
): Promise<{
|
||||
commits: CommitHistoryItem[];
|
||||
total: number;
|
||||
hasMore: boolean;
|
||||
}> => {
|
||||
const res = await apiRequest.get<{
|
||||
commits: CommitHistoryItem[];
|
||||
total: number;
|
||||
hasMore: boolean;
|
||||
}>("/api/v1/pit/commits", {
|
||||
params: {
|
||||
environment,
|
||||
path: directory,
|
||||
workspaceId
|
||||
workspaceId,
|
||||
offset,
|
||||
limit,
|
||||
search,
|
||||
sort
|
||||
}
|
||||
});
|
||||
return res.data;
|
||||
@@ -225,15 +241,30 @@ export const useGetFolderCommitsCount = ({
|
||||
export const useGetFolderCommitHistory = ({
|
||||
workspaceId,
|
||||
environment,
|
||||
directory
|
||||
directory,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
search,
|
||||
sort = "desc"
|
||||
}: {
|
||||
workspaceId: string;
|
||||
environment: string;
|
||||
directory: string;
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
search?: string;
|
||||
sort?: "asc" | "desc";
|
||||
}) => {
|
||||
return useQuery({
|
||||
queryKey: commitKeys.history({ workspaceId, environment, directory }),
|
||||
queryFn: () => fetchFolderCommitHistory(workspaceId, environment, directory),
|
||||
queryKey: [
|
||||
commitKeys.history({ workspaceId, environment, directory }),
|
||||
offset,
|
||||
limit,
|
||||
search,
|
||||
sort
|
||||
],
|
||||
queryFn: () =>
|
||||
fetchFolderCommitHistory(workspaceId, environment, directory, offset, limit, search, sort),
|
||||
enabled: Boolean(workspaceId && environment)
|
||||
});
|
||||
};
|
||||
|
||||
@@ -11,7 +11,8 @@ export enum ApprovalStatus {
|
||||
export enum CommitType {
|
||||
DELETE = "delete",
|
||||
UPDATE = "update",
|
||||
CREATE = "create"
|
||||
CREATE = "create",
|
||||
ADD = "add"
|
||||
}
|
||||
|
||||
export type TSecretApprovalSecChangeData = {
|
||||
|
||||
+4
-3
@@ -22,6 +22,7 @@ import {
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { CommitWithChanges } from "@app/hooks/api/folderCommits";
|
||||
import { useCommitRevert, useGetCommitDetails } from "@app/hooks/api/folderCommits/queries";
|
||||
import { CommitType } from "@app/hooks/api/types";
|
||||
|
||||
import { SecretVersionDiffView } from "../SecretVersionDiffView";
|
||||
import { MergedItem } from "./types";
|
||||
@@ -147,9 +148,9 @@ export const CommitDetailsTab = ({
|
||||
const commitChanges = parsedCommitDetails.changes?.changes || [];
|
||||
|
||||
// Separate changes by type
|
||||
const addedChanges = commitChanges.filter((c) => c.changeType === "add" && !c.isUpdate);
|
||||
const updatedChanges = commitChanges.filter((c) => c.changeType === "add" && c.isUpdate);
|
||||
const deletedChanges = commitChanges.filter((c) => c.changeType === "delete");
|
||||
const addedChanges = commitChanges.filter((c) => c.changeType === CommitType.ADD && !c.isUpdate);
|
||||
const updatedChanges = commitChanges.filter((c) => c.changeType === CommitType.ADD && c.isUpdate);
|
||||
const deletedChanges = commitChanges.filter((c) => c.changeType === CommitType.DELETE);
|
||||
|
||||
// Create merged item list from changes only
|
||||
const changedItems: MergedItem[] = [];
|
||||
|
||||
+112
-74
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
faArrowDownWideShort,
|
||||
faArrowUpWideShort,
|
||||
@@ -144,41 +144,68 @@ export const CommitHistoryTab = ({
|
||||
secretPath: string;
|
||||
}) => {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
const [sortDirection, setSortDirection] = useState<"asc" | "desc">("desc");
|
||||
const [visibleCommits, setVisibleCommits] = useState(10);
|
||||
const [offset, setOffset] = useState(0);
|
||||
const [allCommits, setAllCommits] = useState<Commit[]>([]);
|
||||
const debounceTimeoutRef = useRef<NodeJS.Timeout>();
|
||||
const limit = 5;
|
||||
|
||||
const { data: commits, isLoading } = useGetFolderCommitHistory({
|
||||
// Debounce search term
|
||||
useEffect(() => {
|
||||
if (debounceTimeoutRef.current) {
|
||||
clearTimeout(debounceTimeoutRef.current);
|
||||
}
|
||||
|
||||
debounceTimeoutRef.current = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500);
|
||||
|
||||
return () => {
|
||||
if (debounceTimeoutRef.current) {
|
||||
clearTimeout(debounceTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, [searchTerm]);
|
||||
|
||||
const {
|
||||
data: response,
|
||||
isLoading,
|
||||
isFetching
|
||||
} = useGetFolderCommitHistory({
|
||||
workspaceId: projectId,
|
||||
environment,
|
||||
directory: secretPath
|
||||
directory: secretPath,
|
||||
offset,
|
||||
limit,
|
||||
search: debouncedSearchTerm,
|
||||
sort: sortDirection
|
||||
});
|
||||
|
||||
const filteredCommits = useMemo(() => {
|
||||
if (!commits?.length) return [];
|
||||
const commits = response?.commits || [];
|
||||
const hasMore = response?.hasMore || false;
|
||||
|
||||
return commits.filter(
|
||||
(commit) =>
|
||||
commit.id?.includes(searchTerm) ||
|
||||
commit.message?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
commit.actorMetadata?.name?.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
}, [commits, searchTerm]);
|
||||
// Reset accumulated commits when search or sort changes
|
||||
useEffect(() => {
|
||||
setAllCommits([]);
|
||||
setOffset(0);
|
||||
}, [debouncedSearchTerm, sortDirection]);
|
||||
|
||||
const sortedCommits = useMemo(() => {
|
||||
if (!filteredCommits?.length) return [];
|
||||
// Accumulate commits instead of replacing them
|
||||
useEffect(() => {
|
||||
if (commits.length > 0) {
|
||||
if (offset === 0) {
|
||||
// First load or after search/sort change - replace all commits
|
||||
setAllCommits(commits);
|
||||
} else {
|
||||
// Subsequent loads - append new commits
|
||||
setAllCommits((prev) => [...prev, ...commits]);
|
||||
}
|
||||
}
|
||||
}, [commits, offset]);
|
||||
|
||||
return [...filteredCommits].sort((a, b) => {
|
||||
const dateA = new Date(a.createdAt).getTime();
|
||||
const dateB = new Date(b.createdAt).getTime();
|
||||
return sortDirection === "desc" ? dateB - dateA : dateA - dateB;
|
||||
});
|
||||
}, [filteredCommits, sortDirection]);
|
||||
|
||||
const displayedCommits = useMemo(() => {
|
||||
return sortedCommits.slice(0, visibleCommits);
|
||||
}, [sortedCommits, visibleCommits]);
|
||||
const groupedCommits = useMemo(() => {
|
||||
return displayedCommits.reduce(
|
||||
return allCommits.reduce(
|
||||
(acc, commit) => {
|
||||
const date = format(new Date(commit.createdAt), "MMM d, yyyy");
|
||||
if (!acc[date]) {
|
||||
@@ -189,25 +216,21 @@ export const CommitHistoryTab = ({
|
||||
},
|
||||
{} as Record<string, Commit[]>
|
||||
);
|
||||
}, [displayedCommits]);
|
||||
}, [allCommits]);
|
||||
|
||||
const handleSort = useCallback(() => {
|
||||
setSortDirection((prev) => (prev === "desc" ? "asc" : "desc"));
|
||||
}, []);
|
||||
|
||||
const loadMoreCommits = useCallback(() => {
|
||||
setVisibleCommits((prev) => prev + 10);
|
||||
const handleSearch = useCallback((value: string) => {
|
||||
setSearchTerm(value);
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex h-64 items-center justify-center">
|
||||
<Spinner size="lg" aria-label="Loading commits" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const hasMoreCommits = sortedCommits.length > visibleCommits;
|
||||
const loadMoreCommits = useCallback(() => {
|
||||
if (hasMore && !isFetching) {
|
||||
setOffset((prev) => prev + limit);
|
||||
}
|
||||
}, [hasMore, isFetching, limit]);
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
@@ -217,7 +240,8 @@ export const CommitHistoryTab = ({
|
||||
<Input
|
||||
placeholder="Search commits..."
|
||||
className="h-10 w-full rounded-md border-transparent bg-zinc-800 pl-9 pr-3 text-sm text-white placeholder-gray-400 focus:border-gray-600 focus:ring-primary-500/20"
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
value={searchTerm}
|
||||
aria-label="Search commits"
|
||||
/>
|
||||
<div className="absolute left-3 top-1/2 -translate-y-1/2 transform text-gray-400">
|
||||
@@ -239,43 +263,57 @@ export const CommitHistoryTab = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-8">
|
||||
{Object.keys(groupedCommits).length > 0 ? (
|
||||
<>
|
||||
{Object.entries(groupedCommits).map(([date, dateCommits]) => (
|
||||
<DateGroup
|
||||
key={date}
|
||||
date={date}
|
||||
commits={dateCommits}
|
||||
onSelectCommit={onSelectCommit}
|
||||
{isLoading && offset === 0 ? (
|
||||
<div className="flex h-64 items-center justify-center">
|
||||
<Spinner size="lg" aria-label="Loading commits" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-8">
|
||||
{Object.keys(groupedCommits).length > 0 ? (
|
||||
<>
|
||||
{Object.entries(groupedCommits).map(([date, dateCommits]) => (
|
||||
<DateGroup
|
||||
key={date}
|
||||
date={date}
|
||||
commits={dateCommits}
|
||||
onSelectCommit={onSelectCommit}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<div className="text-white-400 flex min-h-40 flex-col items-center justify-center rounded-lg bg-zinc-900 py-8 text-center">
|
||||
<FontAwesomeIcon
|
||||
icon={faSearch}
|
||||
className="text-white-500 mb-3 text-3xl"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<div className="text-white-400 flex min-h-40 flex-col items-center justify-center rounded-lg bg-zinc-900 py-8 text-center">
|
||||
<FontAwesomeIcon
|
||||
icon={faSearch}
|
||||
className="text-white-500 mb-3 text-3xl"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p>No matching commits found. Try a different search term.</p>
|
||||
</div>
|
||||
)}
|
||||
<p>No matching commits found. Try a different search term.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasMoreCommits && (
|
||||
<div className="flex justify-center pb-2">
|
||||
<Button
|
||||
variant="outline_bg"
|
||||
size="md"
|
||||
className="rounded-md bg-zinc-900 px-6 py-2 text-sm font-medium text-white transition-colors duration-200 hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
onClick={loadMoreCommits}
|
||||
aria-label="Load more commits"
|
||||
>
|
||||
Load more commits
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{hasMore && (
|
||||
<div className="flex justify-center pb-2">
|
||||
<Button
|
||||
variant="outline_bg"
|
||||
size="md"
|
||||
className="rounded-md bg-zinc-900 px-6 py-2 text-sm font-medium text-white transition-colors duration-200 hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
onClick={loadMoreCommits}
|
||||
disabled={isFetching}
|
||||
aria-label="Load more commits"
|
||||
>
|
||||
{isFetching ? (
|
||||
<>
|
||||
<Spinner size="sm" className="mr-2" />
|
||||
Loading...
|
||||
</>
|
||||
) : (
|
||||
"Load more commits"
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user