Skip to content

Commit

Permalink
UI - GitOps Mode, 3/3 (#26537)
Browse files Browse the repository at this point in the history
## For #26229 

- Remove feature flag
- Undo updates to 4 Policies automation modals to facilitate refactor
being implemented in parallel
- Remaining specs:

**Manage teams:**

![manage-teams](https://github.com/user-attachments/assets/af8d8d10-2add-4d8d-8961-61d0de44b067)

Empty:
<img width="1464" alt="Screenshot 2025-02-21 at 4 27 30 PM"
src="https://github.com/user-attachments/assets/17cf4fc2-cc4e-4f63-8276-3db79b44e9e1"
/>

**Team users:**

![team-users](https://github.com/user-attachments/assets/1bf106c1-bdf7-442c-a957-6c9eea6af14d)
Empty:
<img width="1464" alt="Screenshot 2025-02-21 at 4 29 01 PM"
src="https://github.com/user-attachments/assets/46dd0e44-2af3-4ca7-a0be-628e358a61d7"
/>

**Team agent options:**

![team-agent-options](https://github.com/user-attachments/assets/7d4ee8b6-03c7-48d2-8337-b2c33e50abe9)

**Team settings:**

![team-settings](https://github.com/user-attachments/assets/a67b45fc-a5ce-4267-b8fd-2f1e300d1fd8)


- [x] Changes file added for user-visible changes in `changes/`
- [x] Added/updated automated tests
- [ ] A detailed QA plan exists on the associated ticket (if it isn't
there, work with the product group's QA engineer to add it)
- [x] Manual QA for all new/changed functionality

---------

Co-authored-by: Jacob Shandling <[email protected]>
  • Loading branch information
jacobshandling and Jacob Shandling authored Feb 22, 2025
1 parent 0595485 commit b990b3c
Show file tree
Hide file tree
Showing 25 changed files with 273 additions and 280 deletions.
1 change: 1 addition & 0 deletions changes/25478-GitOps-Mode
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Implement GitOps Mode
81 changes: 52 additions & 29 deletions frontend/components/TableContainer/TableContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import SearchField from "components/forms/fields/SearchField";
import Pagination from "components/Pagination";
import Button from "components/buttons/Button";
import Icon from "components/Icon/Icon";
import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";

import { COLORS } from "styles/var/colors";

import DataTable from "./DataTable/DataTable";
Expand All @@ -30,6 +32,10 @@ interface IRowProps extends Row {
};
}

interface ITableContainerActionButtonProps extends IActionButtonProps {
gitOpsModeCompatible?: boolean;
}

interface ITableContainerProps<T = any> {
columnConfigs: any; // TODO: Figure out type
data: any; // TODO: Figure out type
Expand All @@ -41,7 +47,7 @@ interface ITableContainerProps<T = any> {
defaultPageIndex?: number;
defaultSelectedRows?: Record<string, boolean>;
/** Button visible above the table container next to search bar */
actionButton?: IActionButtonProps;
actionButton?: ITableContainerActionButtonProps;
inputPlaceHolder?: string;
disableActionButton?: boolean;
disableMultiRowSelect?: boolean;
Expand Down Expand Up @@ -284,6 +290,47 @@ const TableContainer = <T,>({
onPaginationChange,
]);

const renderFilterActionButton = () => {
// always !!actionButton here, this is for type checker
if (actionButton) {
if (actionButton.gitOpsModeCompatible) {
return (
<GitOpsModeTooltipWrapper
tipOffset={8}
renderChildren={(disableChildren) => (
<Button
disabled={disableActionButton || disableChildren}
onClick={actionButton.onActionButtonClick}
variant={actionButton.variant || "brand"}
className={`${baseClass}__table-action-button`}
>
<>
{actionButton.buttonText}
{actionButton.iconSvg && <Icon name={actionButton.iconSvg} />}
</>
</Button>
)}
/>
);
}
return (
<Button
disabled={disableActionButton}
onClick={actionButton.onActionButtonClick}
variant={actionButton.variant || "brand"}
className={`${baseClass}__table-action-button`}
>
<>
{actionButton.buttonText}
{actionButton.iconSvg && <Icon name={actionButton.iconSvg} />}
</>
</Button>
);
}
// should never reach here
return null;
};

const renderFilters = useCallback(() => {
const opacity = isLoading ? { opacity: 0.4 } : { opacity: 1 };

Expand All @@ -306,19 +353,7 @@ const TableContainer = <T,>({
</div>

{actionButton && !actionButton.hideButton && (
<div className="stackable-header">
<Button
disabled={disableActionButton}
onClick={actionButton.onActionButtonClick}
variant={actionButton.variant || "brand"}
className={`${baseClass}__table-action-button`}
>
<>
{actionButton.buttonText}
{actionButton.iconSvg && <Icon name={actionButton.iconSvg} />}
</>
</Button>
</div>
<div className="stackable-header">{renderFilterActionButton()}</div>
)}
<div className="stackable-header top-shift-header">
{customControl && customControl()}
Expand Down Expand Up @@ -386,21 +421,9 @@ const TableContainer = <T,>({
</div>
)}
<span className="controls">
{actionButton && !actionButton.hideButton && (
<Button
disabled={disableActionButton}
onClick={actionButton.onActionButtonClick}
variant={actionButton.variant || "brand"}
className={`${baseClass}__table-action-button`}
>
<>
{actionButton.buttonText}
{actionButton.iconSvg && (
<Icon name={actionButton.iconSvg} />
)}
</>
</Button>
)}
{actionButton &&
!actionButton.hideButton &&
renderFilterActionButton()}
{customControl && customControl()}
</span>
</div>
Expand Down
62 changes: 49 additions & 13 deletions frontend/components/buttons/ActionButtons/ActionButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ButtonVariant } from "components/buttons/Button/Button";
import DropdownButton from "components/buttons/DropdownButton";
import Icon from "components/Icon/Icon";
import { IconNames } from "components/icons";
import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";

export interface IActionButtonProps {
type: "primary" | "secondary";
Expand All @@ -15,6 +16,7 @@ export interface IActionButtonProps {
iconSvg?: IconNames;
hideAction?: boolean;
onClick: () => void;
gitOpsModeCompatible?: boolean;
}

interface IProps {
Expand Down Expand Up @@ -52,22 +54,56 @@ const ActionButtons = ({ baseClass, actions }: IProps): JSX.Element => {
<div
className={`${baseClass}__action-buttons--secondary-buttons action-buttons__secondary-buttons`}
>
{secondaryActions.map(
(action) =>
!action.hideAction &&
(action.buttonVariant !== "text-icon" ? (
{secondaryActions.map((action) => {
if (!action.hideAction && action.buttonVariant !== "text-icon") {
if (action.gitOpsModeCompatible) {
return (
<GitOpsModeTooltipWrapper
renderChildren={(disableChildren) => (
<Button
variant={action.buttonVariant}
onClick={action.onClick}
disabled={disableChildren}
>
{action.label}
</Button>
)}
/>
);
}
return (
<Button variant={action.buttonVariant} onClick={action.onClick}>
{action.label}
</Button>
) : (
<Button variant="text-icon" onClick={action.onClick}>
<>
{action.label}
{action.iconSvg && <Icon name={action.iconSvg} />}
</>
</Button>
))
)}
);
}
if (action.gitOpsModeCompatible) {
return (
<GitOpsModeTooltipWrapper
renderChildren={(disableChildren) => (
<Button
variant="text-icon"
onClick={action.onClick}
disabled={disableChildren}
>
<>
{action.label}
{action.iconSvg && <Icon name={action.iconSvg} />}
</>
</Button>
)}
/>
);
}
return (
<Button variant="text-icon" onClick={action.onClick}>
<>
{action.label}
{action.iconSvg && <Icon name={action.iconSvg} />}
</>
</Button>
);
})}
</div>
<div
className={`${baseClass}__action-buttons--secondary-dropdown action-buttons__secondary-dropdown`}
Expand Down
9 changes: 3 additions & 6 deletions frontend/pages/admin/IntegrationsPage/IntegrationNavItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,12 @@ const integrationSettingsNavItems: ISideNavItem<any>[] = [
path: PATHS.ADMIN_INTEGRATIONS_CALENDARS,
Card: Calendars,
},
];

if (featureFlags.allowGitOpsMode === "true") {
integrationSettingsNavItems.push({
{
title: "Change management",
urlSection: "change-management",
path: PATHS.ADMIN_INTEGRATIONS_CHANGE_MANAGEMENT,
Card: ChangeManagement,
});
}
},
];

export default integrationSettingsNavItems;
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,15 @@ const TeamDetailsWrapper = ({
buttonVariant: "text-icon",
iconSvg: "eye",
onClick: toggleManageEnrollSecretsModal,
gitOpsModeCompatible: true,
},
{
type: "secondary",
label: "Rename team",
buttonVariant: "text-icon",
iconSvg: "pencil",
onClick: toggleRenameTeamModal,
gitOpsModeCompatible: true,
},
{
type: "secondary",
Expand All @@ -443,6 +445,7 @@ const TeamDetailsWrapper = ({
iconSvg: "trash",
hideAction: !isGlobalAdmin,
onClick: toggleDeleteTeamModal,
gitOpsModeCompatible: true,
},
]}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import SectionHeader from "components/SectionHeader";
// @ts-ignore
import Dropdown from "components/forms/fields/Dropdown";
import Checkbox from "components/forms/fields/Checkbox";
import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper";

import TeamHostExpiryToggle from "./components/TeamHostExpiryToggle";

Expand Down Expand Up @@ -149,7 +150,8 @@ const TeamSettings = ({ location, router }: ITeamSubnavProps) => {
host_expiry_enabled: globalHostExpiryEnabled,
host_expiry_window: globalHostExpiryWindow,
},
} = appConfig ?? { host_expiry_settings: {} };
gitops: { gitops_mode_enabled: gitopsModeEnabled },
} = appConfig ?? { host_expiry_settings: {}, gitops: {} };

const {
data: teamConfig,
Expand Down Expand Up @@ -296,6 +298,7 @@ const TeamSettings = ({ location, router }: ITeamSubnavProps) => {
value={formData.teamHostStatusWebhookEnabled}
helpText="This will trigger webhooks specific to this team, separate from the global host status webhook."
tooltipContent="Send an alert if a portion of your hosts go offline."
disabled={gitopsModeEnabled}
>
Enable host status webhook
</Checkbox>
Expand All @@ -316,6 +319,7 @@ const TeamSettings = ({ location, router }: ITeamSubnavProps) => {
value={formData.teamHostStatusWebhookDestinationUrl}
parseTarget
error={formErrors.host_status_webhook_destination_url}
disabled={gitopsModeEnabled}
tooltip={
<p>
Provide a URL to deliver <br />
Expand All @@ -331,6 +335,7 @@ const TeamSettings = ({ location, router }: ITeamSubnavProps) => {
value={formData.teamHostStatusWebhookHostPercentage}
parseTarget
searchable={false}
disabled={gitopsModeEnabled}
tooltip={
<p>
Select the minimum percentage of hosts that
Expand All @@ -348,6 +353,7 @@ const TeamSettings = ({ location, router }: ITeamSubnavProps) => {
name="teamHostStatusWebhookWindow"
value={formData.teamHostStatusWebhookWindow}
parseTarget
disabled={gitopsModeEnabled}
searchable={false}
tooltip={
<p>
Expand All @@ -372,6 +378,7 @@ const TeamSettings = ({ location, router }: ITeamSubnavProps) => {
setTeamExpiryEnabled={(isEnabled: boolean) =>
onInputChange({ name: "teamHostExpiryEnabled", value: isEnabled })
}
gitopsModeEnabled={gitopsModeEnabled}
/>
)}
{formData.teamHostExpiryEnabled && (
Expand All @@ -385,17 +392,23 @@ const TeamSettings = ({ location, router }: ITeamSubnavProps) => {
name="teamHostExpiryWindow"
value={formData.teamHostExpiryWindow}
error={formErrors.host_expiry_window}
disabled={gitopsModeEnabled}
/>
)}
<Button
type="submit"
variant="brand"
className="button-wrap"
isLoading={updatingTeamSettings}
disabled={Object.keys(formErrors).length > 0}
>
Save
</Button>
<GitOpsModeTooltipWrapper
tipOffset={-8}
renderChildren={(disableChildren) => (
<Button
type="submit"
variant="brand"
className="button-wrap"
isLoading={updatingTeamSettings}
disabled={Object.keys(formErrors).length > 0 || disableChildren}
>
Save
</Button>
)}
/>
</form>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ interface ITeamHostExpiryToggle {
globalHostExpiryWindow?: number;
teamExpiryEnabled: boolean;
setTeamExpiryEnabled: (value: boolean) => void;
gitopsModeEnabled?: boolean;
}

const TeamHostExpiryToggle = ({
globalHostExpiryEnabled,
globalHostExpiryWindow,
teamExpiryEnabled,
setTeamExpiryEnabled,
gitopsModeEnabled,
}: ITeamHostExpiryToggle) => {
const renderHelpText = () =>
// this will never be rendered while globalHostExpiryWindow is undefined
Expand Down Expand Up @@ -49,7 +51,7 @@ const TeamHostExpiryToggle = ({
name="enableHostExpiry"
onChange={setTeamExpiryEnabled}
value={teamExpiryEnabled || globalHostExpiryEnabled} // Still shows checkmark if global expiry is enabled though the checkbox will be disabled.
disabled={globalHostExpiryEnabled}
disabled={globalHostExpiryEnabled || gitopsModeEnabled}
helpText={renderHelpText()}
tooltipContent={
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ const UsersPage = ({ location, router }: ITeamSubnavProps): JSX.Element => {
? toggleAddUserModal
: toggleCreateUserModal,
hideButton: userIds.length === 0 && searchString === "",
gitOpsModeCompatible: true,
}}
onQueryChange={({ searchQuery }) => setSearchString(searchQuery)}
inputPlaceHolder="Search"
Expand Down
Loading

0 comments on commit b990b3c

Please sign in to comment.