mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 06:25:45 +01:00
Improve repository overview
- Sort repositories alphanumerically case insensitive per namespace - Make the namespaces collapsible and store the collapsed state in local storage Committed-by: Konstantin Schaper <konstantin.schaper@cloudogu.com>
This commit is contained in:
@@ -65,6 +65,7 @@ export * from "./usePluginCenterAuthInfo";
|
||||
export * from "./compare";
|
||||
export * from "./utils";
|
||||
export * from "./links";
|
||||
export * from "./localStorage";
|
||||
export { useNamespaceOptions, useGroupOptions, useUserOptions } from "./useAutocompleteOptions";
|
||||
|
||||
export { default as ApiProvider } from "./ApiProvider";
|
||||
|
||||
45
scm-ui/ui-api/src/localStorage.ts
Normal file
45
scm-ui/ui-api/src/localStorage.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function useLocalStorage<T>(
|
||||
key: string,
|
||||
initialValue: T
|
||||
): [value: T, setValue: (value: T | ((previousConfig: T) => T)) => void] {
|
||||
const [value, setValue] = useState<T>(() => {
|
||||
try {
|
||||
const item = localStorage.getItem(key);
|
||||
return item ? JSON.parse(item) : initialValue;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
return initialValue;
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => localStorage.setItem(key, JSON.stringify(value)), [key, value]);
|
||||
|
||||
return [value, setValue];
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
Repository,
|
||||
RepositoryCollection,
|
||||
RepositoryCreation,
|
||||
RepositoryTypeCollection
|
||||
RepositoryTypeCollection,
|
||||
} from "@scm-manager/ui-types";
|
||||
import { useMutation, useQuery, useQueryClient } from "react-query";
|
||||
import { apiClient } from "./apiclient";
|
||||
@@ -72,7 +72,7 @@ export const useRepositories = (request?: UseRepositoriesRequest): ApiResult<Rep
|
||||
}
|
||||
return useQuery<RepositoryCollection, Error>(
|
||||
["repositories", request?.namespace?.namespace, request?.search || "", request?.page || 0],
|
||||
() => apiClient.get(`${link}?${createQueryString(queryParams)}`).then(response => response.json()),
|
||||
() => apiClient.get(`${link}?${createQueryString(queryParams)}`).then((response) => response.json()),
|
||||
{
|
||||
enabled: !request?.disabled,
|
||||
onSuccess: (repositories: RepositoryCollection) => {
|
||||
@@ -80,7 +80,7 @@ export const useRepositories = (request?: UseRepositoriesRequest): ApiResult<Rep
|
||||
repositories._embedded?.repositories.forEach((repository: Repository) => {
|
||||
queryClient.setQueryData(["repository", repository.namespace, repository.name], repository);
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -98,14 +98,14 @@ const createRepository = (link: string) => {
|
||||
}
|
||||
return apiClient
|
||||
.post(createLink, request.repository, "application/vnd.scmm-repository+json;v=2")
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
const location = response.headers.get("Location");
|
||||
if (!location) {
|
||||
throw new Error("Server does not return required Location header");
|
||||
}
|
||||
return apiClient.get(location);
|
||||
})
|
||||
.then(response => response.json());
|
||||
.then((response) => response.json());
|
||||
};
|
||||
};
|
||||
|
||||
@@ -117,10 +117,10 @@ export const useCreateRepository = () => {
|
||||
const { mutate, data, isLoading, error } = useMutation<Repository, Error, CreateRepositoryRequest>(
|
||||
createRepository(link),
|
||||
{
|
||||
onSuccess: repository => {
|
||||
onSuccess: (repository) => {
|
||||
queryClient.setQueryData(["repository", repository.namespace, repository.name], repository);
|
||||
return queryClient.invalidateQueries(["repositories"]);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
return {
|
||||
@@ -129,7 +129,7 @@ export const useCreateRepository = () => {
|
||||
},
|
||||
isLoading,
|
||||
error,
|
||||
repository: data
|
||||
repository: data,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -139,7 +139,7 @@ export const useRepositoryTypes = () => useIndexJsonResource<RepositoryTypeColle
|
||||
export const useRepository = (namespace: string, name: string): ApiResult<Repository> => {
|
||||
const link = useRequiredIndexLink("repositories");
|
||||
return useQuery<Repository, Error>(["repository", namespace, name], () =>
|
||||
apiClient.get(concat(link, namespace, name)).then(response => response.json())
|
||||
apiClient.get(concat(link, namespace, name)).then((response) => response.json())
|
||||
);
|
||||
};
|
||||
|
||||
@@ -150,7 +150,7 @@ export type UseDeleteRepositoryOptions = {
|
||||
export const useDeleteRepository = (options?: UseDeleteRepositoryOptions) => {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutate, isLoading, error, data } = useMutation<unknown, Error, Repository>(
|
||||
repository => {
|
||||
(repository) => {
|
||||
const link = requiredLink(repository, "delete");
|
||||
return apiClient.delete(link);
|
||||
},
|
||||
@@ -161,21 +161,21 @@ export const useDeleteRepository = (options?: UseDeleteRepositoryOptions) => {
|
||||
}
|
||||
queryClient.removeQueries(repoQueryKey(repository));
|
||||
await queryClient.invalidateQueries(["repositories"]);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
return {
|
||||
remove: (repository: Repository) => mutate(repository),
|
||||
isLoading,
|
||||
error,
|
||||
isDeleted: !!data
|
||||
isDeleted: !!data,
|
||||
};
|
||||
};
|
||||
|
||||
export const useUpdateRepository = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutate, isLoading, error, data } = useMutation<unknown, Error, Repository>(
|
||||
repository => {
|
||||
(repository) => {
|
||||
const link = requiredLink(repository, "update");
|
||||
return apiClient.put(link, repository, "application/vnd.scmm-repository+json;v=2");
|
||||
},
|
||||
@@ -183,21 +183,21 @@ export const useUpdateRepository = () => {
|
||||
onSuccess: async (_, repository) => {
|
||||
await queryClient.invalidateQueries(repoQueryKey(repository));
|
||||
await queryClient.invalidateQueries(["repositories"]);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
return {
|
||||
update: (repository: Repository) => mutate(repository),
|
||||
isLoading,
|
||||
error,
|
||||
isUpdated: !!data
|
||||
isUpdated: !!data,
|
||||
};
|
||||
};
|
||||
|
||||
export const useArchiveRepository = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutate, isLoading, error, data } = useMutation<unknown, Error, Repository>(
|
||||
repository => {
|
||||
(repository) => {
|
||||
const link = requiredLink(repository, "archive");
|
||||
return apiClient.post(link);
|
||||
},
|
||||
@@ -205,21 +205,21 @@ export const useArchiveRepository = () => {
|
||||
onSuccess: async (_, repository) => {
|
||||
await queryClient.invalidateQueries(repoQueryKey(repository));
|
||||
await queryClient.invalidateQueries(["repositories"]);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
return {
|
||||
archive: (repository: Repository) => mutate(repository),
|
||||
isLoading,
|
||||
error,
|
||||
isArchived: !!data
|
||||
isArchived: !!data,
|
||||
};
|
||||
};
|
||||
|
||||
export const useUnarchiveRepository = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutate, isLoading, error, data } = useMutation<unknown, Error, Repository>(
|
||||
repository => {
|
||||
(repository) => {
|
||||
const link = requiredLink(repository, "unarchive");
|
||||
return apiClient.post(link);
|
||||
},
|
||||
@@ -227,35 +227,35 @@ export const useUnarchiveRepository = () => {
|
||||
onSuccess: async (_, repository) => {
|
||||
await queryClient.invalidateQueries(repoQueryKey(repository));
|
||||
await queryClient.invalidateQueries(["repositories"]);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
return {
|
||||
unarchive: (repository: Repository) => mutate(repository),
|
||||
isLoading,
|
||||
error,
|
||||
isUnarchived: !!data
|
||||
isUnarchived: !!data,
|
||||
};
|
||||
};
|
||||
|
||||
export const useRunHealthCheck = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutate, isLoading, error, data } = useMutation<unknown, Error, Repository>(
|
||||
repository => {
|
||||
(repository) => {
|
||||
const link = requiredLink(repository, "runHealthCheck");
|
||||
return apiClient.post(link);
|
||||
},
|
||||
{
|
||||
onSuccess: async (_, repository) => {
|
||||
await queryClient.invalidateQueries(repoQueryKey(repository));
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
return {
|
||||
runHealthCheck: (repository: Repository) => mutate(repository),
|
||||
isLoading,
|
||||
error,
|
||||
isRunning: !!data
|
||||
isRunning: !!data,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -264,7 +264,7 @@ export const useExportInfo = (repository: Repository): ApiResultWithFetching<Exp
|
||||
//TODO Refetch while exporting to update the page
|
||||
const { isLoading, isFetching, error, data } = useQuery<ExportInfo, Error>(
|
||||
["repository", repository.namespace, repository.name, "exportInfo"],
|
||||
() => apiClient.get(link).then(response => response.json()),
|
||||
() => apiClient.get(link).then((response) => response.json()),
|
||||
{}
|
||||
);
|
||||
|
||||
@@ -272,7 +272,7 @@ export const useExportInfo = (repository: Repository): ApiResultWithFetching<Exp
|
||||
isLoading,
|
||||
isFetching,
|
||||
error: error instanceof NotFoundError ? null : error,
|
||||
data
|
||||
data,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -315,14 +315,14 @@ export const useExportRepository = () => {
|
||||
const id = setInterval(() => {
|
||||
apiClient
|
||||
.get(infolink)
|
||||
.then(r => r.json())
|
||||
.then((r) => r.json())
|
||||
.then((info: ExportInfo) => {
|
||||
if (info._links.download) {
|
||||
clearInterval(id);
|
||||
resolve(info);
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
.catch((e) => {
|
||||
clearInterval(id);
|
||||
reject(e);
|
||||
});
|
||||
@@ -335,21 +335,21 @@ export const useExportRepository = () => {
|
||||
onSuccess: async (_, { repository }) => {
|
||||
await queryClient.invalidateQueries(repoQueryKey(repository));
|
||||
await queryClient.invalidateQueries(["repositories"]);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
return {
|
||||
exportRepository: (repository: Repository, options: ExportOptions) => mutate({ repository, options }),
|
||||
isLoading,
|
||||
error,
|
||||
data
|
||||
data,
|
||||
};
|
||||
};
|
||||
|
||||
export const usePaths = (repository: Repository, revision: string): ApiResult<Paths> => {
|
||||
const link = requiredLink(repository, "paths").replace("{revision}", revision);
|
||||
return useQuery<Paths, Error>(repoQueryKey(repository, "paths", revision), () =>
|
||||
apiClient.get(link).then(response => response.json())
|
||||
apiClient.get(link).then((response) => response.json())
|
||||
);
|
||||
};
|
||||
|
||||
@@ -370,7 +370,7 @@ export const useRenameRepository = (repository: Repository) => {
|
||||
const { mutate, isLoading, error, data } = useMutation<unknown, Error, RenameRepositoryRequest>(
|
||||
({ name, namespace }) => apiClient.post(url, { namespace, name }, "application/vnd.scmm-repository+json;v=2"),
|
||||
{
|
||||
onSuccess: () => queryClient.removeQueries(repoQueryKey(repository))
|
||||
onSuccess: () => queryClient.removeQueries(repoQueryKey(repository)),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -378,7 +378,7 @@ export const useRenameRepository = (repository: Repository) => {
|
||||
renameRepository: (namespace: string, name: string) => mutate({ namespace, name }),
|
||||
isLoading,
|
||||
error,
|
||||
isRenamed: !!data
|
||||
isRenamed: !!data,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user