Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add repository action button #4131

Merged
merged 4 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions frontend/app/src/components/buttons/button-primitive.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { focusStyle } from "@/components/ui/style";
import { Tooltip, TooltipProps } from "@/components/ui/tooltip";
import LoadingScreen from "@/screens/loading-screen/loading-screen";
import { classNames } from "@/utils/common";
import { cva, type VariantProps } from "class-variance-authority";
import { ButtonHTMLAttributes, forwardRef } from "react";
import { Link, LinkProps } from "react-router-dom";
import { Spinner } from "@/components/ui/spinner";

const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium disabled:opacity-60 disabled:cursor-disabled",
Expand Down Expand Up @@ -46,8 +46,8 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
className={classNames(focusStyle, buttonVariants({ variant, size, className }))}
ref={ref}
{...props}>
{isLoading && <LoadingScreen hideText size={8} />}
{!isLoading && children}
{isLoading && <Spinner className="mr-2" />}
{children}
</button>
);
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/src/components/ui/dropdown-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const DropdownMenuItem = forwardRef<
<DropdownMenuPrimitive.Item
ref={ref}
className={classNames(
"rounded-sm px-2 py-1.5 text-xs",
"rounded-sm px-2 py-1.5 text-sm",
"relative flex items-center",
"cursor-pointer outline-none focus:bg-gray-100",
"data-[disabled]:pointer-events-none data-[disabled]:opacity-40",
Expand Down
2 changes: 2 additions & 0 deletions frontend/app/src/config/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export const DATA_CHECK_OBJECT = "CoreDataCheck";

export const ACCOUNT_OBJECT = "CoreGenericAccount";

export const GENERIC_REPOSITORY_KIND = "CoreGenericRepository";

export const REPOSITORY_KIND = "CoreRepository";

export const READONLY_REPOSITORY_KIND = "CoreReadOnlyRepository";
Expand Down
18 changes: 18 additions & 0 deletions frontend/app/src/graphql/mutations/repository/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { gql } from "@apollo/client";

export const CHECK_REPOSITORY_CONNECTIVITY = gql`
mutation CHECK_REPOSITORY_CONNECTIVITY($repositoryId: String!) {
InfrahubRepositoryConnectivity(data: { id: $repositoryId }) {
message
ok
}
}
`;

export const REIMPORT_LAST_COMMIT = gql`
mutation REIMPORT_LAST_COMMIT($repositoryId: String!) {
InfrahubRepositoryProcess(data: { id: $repositoryId }) {
ok
}
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Link } from "@/components/ui/link";
import {
ARTIFACT_DEFINITION_OBJECT,
DEFAULT_BRANCH_NAME,
GENERIC_REPOSITORY_KIND,
MENU_EXCLUDELIST,
TASK_TAB,
TASK_TARGET,
Expand Down Expand Up @@ -44,6 +45,8 @@ import RelationshipDetails from "./relationship-details-paginated";
import { RelationshipsDetails } from "./relationships-details-paginated";
import graphqlClient from "@/graphql/graphqlClientApollo";
import { GroupsManagerTriggerButton } from "@/screens/groups/groups-manager-trigger-button";
import { isGeneric } from "@/utils/common";
import RepositoryActionMenu from "@/screens/repository/repository-action-menu";

type ObjectDetailsProps = {
schema: IModelSchema;
Expand Down Expand Up @@ -131,15 +134,15 @@ export default function ObjectItemDetails({
<Tabs
tabs={tabs}
rightItems={
<>
<div className="flex items-center gap-1">
{schema.kind === ARTIFACT_DEFINITION_OBJECT && <Generate />}

<ButtonWithTooltip
disabled={!permission.write.allow}
tooltipEnabled
tooltipContent={permission.write.message ?? "Edit object"}
onClick={() => setShowEditDrawer(true)}
className="mr-4 rounded-full text-custom-blue-600 p-4"
className="rounded-full text-custom-blue-600 p-4"
variant={"outline"}
size={"icon"}
data-testid="edit-button">
Expand All @@ -153,7 +156,11 @@ export default function ObjectItemDetails({
className="text-custom-blue-600 p-4"
/>
)}
</>

{!isGeneric(schema) && schema.inherit_from?.includes(GENERIC_REPOSITORY_KIND) && (
<RepositoryActionMenu repositoryId={objectDetailsData.id} />
)}
</div>
}
/>
)}
Expand Down
156 changes: 156 additions & 0 deletions frontend/app/src/screens/repository/repository-action-menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { Icon } from "@iconify-icon/react";

import { Button, ButtonWithTooltip } from "@/components/buttons/button-primitive";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useMutation } from "@/hooks/useQuery";
import {
CHECK_REPOSITORY_CONNECTIVITY,
REIMPORT_LAST_COMMIT,
} from "@/graphql/mutations/repository/actions";
import { toast } from "react-toastify";
import { Alert, ALERT_TYPES } from "@/components/ui/alert";
import { useState } from "react";
import { Dialog } from "@headlessui/react";

const RepositoryActionMenu = ({ repositoryId }: { repositoryId: string }) => {
const [isOpen, setIsOpen] = useState(false);

return (
<>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<ButtonWithTooltip variant="outline" size="icon" className="p-4">
<Icon icon="mdi:dots-vertical" className="text-custom-blue-800 text-lg p-4" />
</ButtonWithTooltip>
</DropdownMenuTrigger>

<DropdownMenuContent>
<DropdownMenuItem onClick={() => setIsOpen(true)}>
<Icon icon="mdi:wifi-check" className="text-lg mr-2" />
Check connectivity
</DropdownMenuItem>

<ReimportLastCommitAction repositoryId={repositoryId} />
</DropdownMenuContent>
</DropdownMenu>

<CheckConnectivityModal repositoryId={repositoryId} isOpen={isOpen} setIsOpen={setIsOpen} />
</>
);
};

const CheckConnectivityModal = ({
isOpen,
setIsOpen,
repositoryId,
}: {
isOpen: boolean;
setIsOpen: (b: boolean) => void;
repositoryId: string;
}) => {
const [checkConnectivity, { loading, data, error, called, reset }] = useMutation(
CHECK_REPOSITORY_CONNECTIVITY,
{
variables: { repositoryId },
}
);

const handleClose = () => {
setIsOpen(false);
reset();
};

const isConnectivityOk = data?.InfrahubRepositoryConnectivity?.ok;

return (
<>
<Dialog open={isOpen} onClose={() => setIsOpen(false)}>
<div className="fixed inset-0 flex w-screen items-center justify-center bg-gray-600/25">
<Dialog.Panel className="bg-white p-4 border rounded-lg max-w-lg space-y-4">
<Dialog.Title className="font-semibold text-lg">
Check{loading && "ing"} repository connectivity
</Dialog.Title>

<Dialog.Description>
Are you sure you want to check the connectivity to this repository? This will validate
your connection and authentication status.
</Dialog.Description>

<div className="text-right space-x-2">
<Button variant="outline" onClick={handleClose}>
Cancel
</Button>
<Button isLoading={loading} disabled={loading} onClick={() => checkConnectivity()}>
Check now
</Button>
</div>

<Dialog open={called && !loading} onClose={handleClose}>
<div className="fixed inset-0 flex w-screen items-center justify-center">
<Dialog.Panel className="bg-white p-4 border rounded-lg max-w-lg space-y-4">
<Dialog.Title className="font-semibold text-lg">
Connection {isConnectivityOk ? "Successful" : "Failed"}
</Dialog.Title>

<Dialog.Description>
{data?.InfrahubRepositoryConnectivity?.message || error?.message}
</Dialog.Description>

{isConnectivityOk && (
<Button variant="active" onClick={handleClose}>
Done
</Button>
)}

{!isConnectivityOk && (
<div className="text-right space-x-2">
<Button variant="outline" onClick={handleClose}>
Cancel
</Button>

<Button variant="danger" onClick={() => checkConnectivity()}>
Retry
</Button>
</div>
)}
</Dialog.Panel>
</div>
</Dialog>
</Dialog.Panel>
</div>
</Dialog>
</>
);
};

const ReimportLastCommitAction = ({ repositoryId }: { repositoryId: string }) => {
const [reimportLastCommit] = useMutation(REIMPORT_LAST_COMMIT, {
variables: {
repositoryId,
},
onCompleted: (data) => {
if (data?.InfrahubRepositoryProcess?.ok) {
toast(
<Alert
type={ALERT_TYPES.SUCCESS}
message='Reimport of last commit started. You can view its status on the "Tasks" tab.'
/>
);
}
},
});

return (
<DropdownMenuItem onClick={() => reimportLastCommit()}>
<Icon icon="mdi:replay" className="text-lg mr-2" />
Reimport last commit
</DropdownMenuItem>
);
};

export default RepositoryActionMenu;
Loading