diff --git a/pkg/app/web/src/__fixtures__/dummy-application.ts b/pkg/app/web/src/__fixtures__/dummy-application.ts index 2745723370..a6d9784857 100644 --- a/pkg/app/web/src/__fixtures__/dummy-application.ts +++ b/pkg/app/web/src/__fixtures__/dummy-application.ts @@ -49,5 +49,6 @@ export const dummyApplication: Application = { }, syncState: dummyApplicationSyncState, updatedAt: 0, + deletedAt: 0, deleted: false, }; diff --git a/pkg/app/web/src/components/application-detail.tsx b/pkg/app/web/src/components/application-detail.tsx index f67504f863..5144ffde50 100644 --- a/pkg/app/web/src/components/application-detail.tsx +++ b/pkg/app/web/src/components/application-detail.tsx @@ -10,6 +10,7 @@ import SyncIcon from "@material-ui/icons/Cached"; import OpenInNewIcon from "@material-ui/icons/OpenInNew"; import Skeleton from "@material-ui/lab/Skeleton/Skeleton"; import { SerializedError } from "@reduxjs/toolkit"; +import clsx from "clsx"; import dayjs from "dayjs"; import React, { FC, memo } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -51,6 +52,9 @@ const useStyles = makeStyles((theme) => ({ position: "relative", flexDirection: "column", }, + disabled: { + opacity: 0.6, + }, content: { flex: 1, }, @@ -211,7 +215,13 @@ export const ApplicationDetail: FC = memo(function ApplicationDetail({ } return ( - + @@ -305,6 +315,7 @@ export const ApplicationDetail: FC = memo(function ApplicationDetail({ label="select sync strategy" color="primary" loading={isSyncing} + disabled={isSyncing || Boolean(app?.disabled)} onClick={handleSync} options={syncOptions} startIcon={} diff --git a/pkg/app/web/src/components/application-list.tsx b/pkg/app/web/src/components/application-list.tsx index 8b0826bed3..65c753409c 100644 --- a/pkg/app/web/src/components/application-list.tsx +++ b/pkg/app/web/src/components/application-list.tsx @@ -42,6 +42,7 @@ import { APPLICATION_KIND_TEXT } from "../constants/application-kind"; import { setUpdateTargetId } from "../modules/update-application"; import { DeleteApplicationDialog } from "./delete-application-dialog"; import { setDeletingAppId } from "../modules/delete-application"; +import clsx from "clsx"; const useStyles = makeStyles((theme) => ({ root: { @@ -56,6 +57,9 @@ const useStyles = makeStyles((theme) => ({ statusText: { marginLeft: theme.spacing(1), }, + disabledRow: { + background: theme.palette.grey[200], + }, })); const NOT_AVAILABLE_TEXT = "N/A"; @@ -184,7 +188,10 @@ export const ApplicationList: FC = memo(function ApplicationList() { ).map((app) => { const recentlyDeployment = app.mostRecentlySuccessfulDeployment; return ( - +
{app.syncState ? ( diff --git a/pkg/app/web/src/components/application-state-view.tsx b/pkg/app/web/src/components/application-state-view.tsx index 98f077f8c2..8414b67398 100644 --- a/pkg/app/web/src/components/application-state-view.tsx +++ b/pkg/app/web/src/components/application-state-view.tsx @@ -10,6 +10,10 @@ import React, { FC, memo } from "react"; import { useDispatch, useSelector } from "react-redux"; import { UI_TEXT_REFRESH } from "../constants/ui-text"; import { AppState } from "../modules"; +import { + Application, + selectById as selectAppById, +} from "../modules/applications"; import { ApplicationLiveState, fetchApplicationStateById, @@ -23,6 +27,8 @@ interface Props { } const ERROR_MESSAGE = "It was unable to fetch the latest state of application."; +const DISABLED_APPLICATION_MESSAGE = + "This application is currently disabled. You can enable it from the application list page."; const useStyles = makeStyles(() => ({ container: { @@ -37,14 +43,30 @@ export const ApplicationStateView: FC = memo( function ApplicationStateView({ applicationId }) { const classes = useStyles(); const dispatch = useDispatch(); - const [hasError, liveState] = useSelector< + const [hasError, liveState, app] = useSelector< AppState, - [boolean, ApplicationLiveState | undefined] + [boolean, ApplicationLiveState | undefined, Application | undefined] >((state) => [ selectHasError(state.applicationLiveState, applicationId), selectLiveStateById(state.applicationLiveState, applicationId), + selectAppById(state.applications, applicationId), ]); + if (app?.disabled) { + return ( + + + {DISABLED_APPLICATION_MESSAGE} + + + ); + } + if (hasError) { return ( diff --git a/pkg/app/web/src/components/deployment-detail.tsx b/pkg/app/web/src/components/deployment-detail.tsx index fe26636893..19f1a7e14a 100644 --- a/pkg/app/web/src/components/deployment-detail.tsx +++ b/pkg/app/web/src/components/deployment-detail.tsx @@ -233,6 +233,7 @@ export const DeploymentDetail: FC = memo(function DeploymentDetail({ }} startIcon={} loading={isCanceling} + disabled={isCanceling} /> )} diff --git a/pkg/app/web/src/components/split-button.stories.tsx b/pkg/app/web/src/components/split-button.stories.tsx index dd3bc6f582..b635e40880 100644 --- a/pkg/app/web/src/components/split-button.stories.tsx +++ b/pkg/app/web/src/components/split-button.stories.tsx @@ -14,6 +14,7 @@ export const overview: React.FC = () => ( startIcon={} options={["Cancel", "Cancel Without Rollback"]} onClick={action("onClick")} + disabled={false} loading={false} /> ); @@ -25,5 +26,6 @@ export const loading: React.FC = () => ( options={["Cancel", "Cancel Without Rollback"]} onClick={action("onClick")} loading + disabled /> ); diff --git a/pkg/app/web/src/components/split-button.test.tsx b/pkg/app/web/src/components/split-button.test.tsx index 8d74b214a8..1373d831e8 100644 --- a/pkg/app/web/src/components/split-button.test.tsx +++ b/pkg/app/web/src/components/split-button.test.tsx @@ -1,6 +1,6 @@ import userEvent from "@testing-library/user-event"; import React from "react"; -import { render, screen, act } from "../../test-utils"; +import { act, render, screen } from "../../test-utils"; import { SplitButton } from "./split-button"; it("calls onClick handler with option's index if clicked", () => { @@ -9,6 +9,7 @@ it("calls onClick handler with option's index if clicked", () => { , diff --git a/pkg/app/web/src/components/split-button.tsx b/pkg/app/web/src/components/split-button.tsx index 46b067f1f4..5232347a73 100644 --- a/pkg/app/web/src/components/split-button.tsx +++ b/pkg/app/web/src/components/split-button.tsx @@ -30,6 +30,7 @@ interface Props { label: string; onClick: (index: number) => void; startIcon?: React.ReactNode; + disabled: boolean; loading: boolean; color?: PropTypes.Color; className?: string; @@ -38,6 +39,7 @@ interface Props { export const SplitButton: FC = ({ onClick, options, + disabled, loading, startIcon, className, @@ -55,12 +57,12 @@ export const SplitButton: FC = ({ variant="outlined" color={color || "inherit"} ref={anchorRef} - disabled={loading} + disabled={disabled} >