From 94c0a5c89003794e259395d97335634f99ec4fa5 Mon Sep 17 00:00:00 2001 From: Milad Sadeghi Date: Tue, 26 Nov 2024 22:10:04 +0330 Subject: [PATCH 1/4] refactor(argocd): improve ArgoCD UI for better usability --- web/package.json | 2 + web/pnpm-lock.yaml | 17 +++ .../components/internal-ui/PlatformBox.tsx | 4 +- web/src/features/constants.ts | 5 +- .../features/terraform/components/Argocd.tsx | 143 +++++++++++++++--- web/src/features/terraform/constants.ts | 16 +- web/src/utils/tailwind.ts | 6 + 7 files changed, 152 insertions(+), 41 deletions(-) create mode 100644 web/src/utils/tailwind.ts diff --git a/web/package.json b/web/package.json index 26657ccc..5973ac1c 100644 --- a/web/package.json +++ b/web/package.json @@ -14,6 +14,7 @@ "@emotion/react": "^11.13.3", "@tanstack/react-query": "^5.59.19", "axios": "^1.7.7", + "clsx": "^2.1.1", "i": "^0.3.7", "next-themes": "^0.4.3", "npm": "^10.9.0", @@ -22,6 +23,7 @@ "react-hook-form": "^7.53.2", "react-icons": "^5.3.0", "react-router-dom": "^6.27.0", + "tailwind-merge": "^2.5.5", "uuid": "^11.0.3", "zustand": "^5.0.1" }, diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 3204ebba..6396f351 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: axios: specifier: ^1.7.7 version: 1.7.7 + clsx: + specifier: ^2.1.1 + version: 2.1.1 i: specifier: ^0.3.7 version: 0.3.7 @@ -44,6 +47,9 @@ importers: react-router-dom: specifier: ^6.27.0 version: 6.28.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tailwind-merge: + specifier: ^2.5.5 + version: 2.5.5 uuid: specifier: ^11.0.3 version: 11.0.3 @@ -1018,6 +1024,10 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1812,6 +1822,9 @@ packages: tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + tailwind-merge@2.5.5: + resolution: {integrity: sha512-0LXunzzAZzo0tEPxV3I297ffKZPlKDrjj7NXphC8V5ak9yHC5zRmxnOe2m/Rd/7ivsOMJe3JZ2JVocoDdQTRBA==} + tailwindcss@3.4.15: resolution: {integrity: sha512-r4MeXnfBmSOuKUWmXe6h2CcyfzJCEk4F0pptO5jlnYSIViUkVmsawj80N5h2lO3gwcmSb4n3PuN+e+GC1Guylw==} engines: {node: '>=14.0.0'} @@ -3230,6 +3243,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -3909,6 +3924,8 @@ snapshots: tabbable@6.2.0: {} + tailwind-merge@2.5.5: {} + tailwindcss@3.4.15: dependencies: '@alloc/quick-lru': 5.2.0 diff --git a/web/src/components/internal-ui/PlatformBox.tsx b/web/src/components/internal-ui/PlatformBox.tsx index 1ec285ef..f9e9f718 100644 --- a/web/src/components/internal-ui/PlatformBox.tsx +++ b/web/src/components/internal-ui/PlatformBox.tsx @@ -12,7 +12,7 @@ interface PlatformProps { mapperFunction: (data: FormData) => RequestData; } -const PlatformBox = ({ +const PlatformBox = ({ serviceName, defaultValues, endpoint, @@ -36,7 +36,7 @@ const PlatformBox = ({
-
+

{serviceName}:

{fieldProperties.map((field) => ( diff --git a/web/src/features/constants.ts b/web/src/features/constants.ts index 869dbaa8..4bd61644 100644 --- a/web/src/features/constants.ts +++ b/web/src/features/constants.ts @@ -75,11 +75,8 @@ export enum TerraformIAMFields { export enum TerraformArgocdFields { AUTO_PRUNE = "autoPrune", SELF_HEAL = "selfHeal", - APPLY_OUT_OF_SYNC_ONLY = "applyOutOfSyncOnly", - CREATE_NAMESPACE = "createNamespace", - FAIL_OR_SHARE_RESOURCE = "failOrShareResource", ARGOCD_REPOSITORY = "argocdRepository", - ARGOCD_CLUSTER = "argocdCluster", + APPLICATION_DEPENDS_REPOSITORY = "applicationDependsRepository", } export enum UserType { diff --git a/web/src/features/terraform/components/Argocd.tsx b/web/src/features/terraform/components/Argocd.tsx index 9423825e..8b50638e 100644 --- a/web/src/features/terraform/components/Argocd.tsx +++ b/web/src/features/terraform/components/Argocd.tsx @@ -1,28 +1,129 @@ -import PlatformBox from "../../../components/internal-ui/PlatformBox"; -import useFindPlatform from "../../../hooks/useFindPlatform"; -import { TerraformServices } from "../../constants"; -import { - ApiRequestTerraformArgocd, - TerraformArgocdFormData, -} from "../../models"; +import { useEffect, useState } from "react"; +import { cn } from "../../../utils/tailwind"; +import { FaChevronDown } from "react-icons/fa"; +import apiClient from "../../../utils/apiClient"; +import { Endpoints } from "../../constants"; +import { useMutation } from "@tanstack/react-query"; const Argocd = () => { - const { platform } = useFindPlatform(TerraformServices.ARGOCD); + const [buttons, setButtons] = useState({ + auto_prune: false, + self_heal: false, + argocd_repository: false, + application_depends_repository: false + }); - return ( - <> - {platform && ( - ApiRequestTerraformArgocd + const [menus, setMenus] = useState({ + argocd_application: false, + sync_policy: false + }); + + const [error, setError] = useState(false); + + const {data, isPending, isError, isSuccess, mutate} = useMutation<{output: string}, Error, {body: {argocd_application: {sync_policy: {auto_prune: boolean, self_heal: boolean}}, argocd_repository: boolean, application_depends_repository: boolean}}>({ + mutationKey: ["argocd"], + mutationFn: async ({body}) => { + return await apiClient.post(Endpoints.POST_IAC_ARGOCD, {...body}); + } + }) + + useEffect(() => { + if (isError) { + setError(true); + setTimeout(() => { + setError(false); + }, 3000); + } + }, [isError]); + + useEffect(() => { + if (isSuccess && data) { + const {output} = data; + const link = document.createElement('a'); + link.href = output; + document.body.appendChild(link); + link.click(); + + document.body.removeChild(link); + + } + }, [isSuccess, data]) + + const handleButtons = (button: keyof typeof buttons) => { + setButtons({...buttons, [button]: !buttons[button]}) + } + + const handleMenus = (menu: keyof typeof menus) => { + setMenus({...menus, [menu]: !menus[menu]}) + } + + const handleSubmit = async () => { + const body = { + "argocd_application": { + "sync_policy": { + "auto_prune": buttons.auto_prune, + "self_heal": buttons.self_heal } - serviceName={platform.serviceName} - /> - )} - + }, + "argocd_repository": buttons.argocd_repository, + "application_depends_repository": buttons.application_depends_repository + } + + mutate({body}) + } + + return ( +
+
+
+ +
+ +
+
+
+

Auto Prune

+ handleButtons("auto_prune")} /> +
+
+
+
+

Self Heal

+ handleButtons("self_heal")} /> +
+
+
+
+
+
+

Argocd Repository

+ handleButtons("argocd_repository")} /> +
+
+
+
+

Application Depends Repository

+ handleButtons("application_depends_repository")} /> +
+
+
+
+ +
); }; diff --git a/web/src/features/terraform/constants.ts b/web/src/features/terraform/constants.ts index 0581464a..c283e7dd 100644 --- a/web/src/features/terraform/constants.ts +++ b/web/src/features/terraform/constants.ts @@ -81,25 +81,13 @@ const argocdFieldProperties: Checkboxprops[] = [ fieldName: TerraformArgocdFields.SELF_HEAL, label: "Self heal", }, - { - fieldName: TerraformArgocdFields.APPLY_OUT_OF_SYNC_ONLY, - label: "Apply out of sync only", - }, - { - fieldName: TerraformArgocdFields.CREATE_NAMESPACE, - label: "Create namespace", - }, - { - fieldName: TerraformArgocdFields.FAIL_OR_SHARE_RESOURCE, - label: "Fail or share resource", - }, { fieldName: TerraformArgocdFields.ARGOCD_REPOSITORY, label: "ARGOCD repository", }, { - fieldName: TerraformArgocdFields.ARGOCD_CLUSTER, - label: "ARGOCD cluster", + fieldName: TerraformArgocdFields.APPLICATION_DEPENDS_REPOSITORY , + label: "Application depends repository", }, ]; diff --git a/web/src/utils/tailwind.ts b/web/src/utils/tailwind.ts new file mode 100644 index 00000000..2430735f --- /dev/null +++ b/web/src/utils/tailwind.ts @@ -0,0 +1,6 @@ +import clsx, { ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export const cn = (...inputs: ClassValue[]) => { + return twMerge(clsx(inputs)); +}; From 7503f4a487e980d3f83c3deb76f1f5235737ea46 Mon Sep 17 00:00:00 2001 From: Milad Sadeghi Date: Tue, 26 Nov 2024 23:28:37 +0330 Subject: [PATCH 2/4] feat(argocd): enable file download after user submission --- .../features/terraform/components/Argocd.tsx | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/web/src/features/terraform/components/Argocd.tsx b/web/src/features/terraform/components/Argocd.tsx index 8b50638e..2225d9a5 100644 --- a/web/src/features/terraform/components/Argocd.tsx +++ b/web/src/features/terraform/components/Argocd.tsx @@ -4,6 +4,7 @@ import { FaChevronDown } from "react-icons/fa"; import apiClient from "../../../utils/apiClient"; import { Endpoints } from "../../constants"; import { useMutation } from "@tanstack/react-query"; +import { AxiosResponse } from "axios"; const Argocd = () => { const [buttons, setButtons] = useState({ @@ -20,10 +21,11 @@ const Argocd = () => { const [error, setError] = useState(false); - const {data, isPending, isError, isSuccess, mutate} = useMutation<{output: string}, Error, {body: {argocd_application: {sync_policy: {auto_prune: boolean, self_heal: boolean}}, argocd_repository: boolean, application_depends_repository: boolean}}>({ + const {data, isPending, isError, isSuccess, mutate} = useMutation({ mutationKey: ["argocd"], mutationFn: async ({body}) => { - return await apiClient.post(Endpoints.POST_IAC_ARGOCD, {...body}); + await apiClient.post(Endpoints.POST_IAC_ARGOCD, {...body}); + return await apiClient.get(`${Endpoints.GET_DOWNLOAD_TERRAFORM}MyTerraform/argocd`, {responseType: "blob"}); } }) @@ -38,14 +40,13 @@ const Argocd = () => { useEffect(() => { if (isSuccess && data) { - const {output} = data; - const link = document.createElement('a'); - link.href = output; - document.body.appendChild(link); - link.click(); - - document.body.removeChild(link); - + const blob = new Blob([data.data], { type: data.headers['content-type'] }); + const link = document.createElement('a'); + link.href = URL.createObjectURL(blob); + link.download = "Argpcd.zip"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); } }, [isSuccess, data]) @@ -91,7 +92,7 @@ const Argocd = () => {

Auto Prune

handleButtons("auto_prune")} /> + }) } defaultChecked={buttons.auto_prune} onClick={() => handleButtons("auto_prune")} />
@@ -99,7 +100,7 @@ const Argocd = () => {

Self Heal

handleButtons("self_heal")} /> + }) } defaultChecked={buttons.self_heal} onClick={() => handleButtons("self_heal")} />
@@ -109,7 +110,7 @@ const Argocd = () => {

Argocd Repository

handleButtons("argocd_repository")} /> + }) } defaultChecked={buttons.argocd_repository} onClick={() => handleButtons("argocd_repository")} />
@@ -117,7 +118,7 @@ const Argocd = () => {

Application Depends Repository

handleButtons("application_depends_repository")} /> + }) } defaultChecked={buttons.application_depends_repository} onClick={() => handleButtons("application_depends_repository")} />
From 3123f724f8574f7580a62a3cce447520064272be Mon Sep 17 00:00:00 2001 From: Milad Sadeghi Date: Tue, 26 Nov 2024 23:36:42 +0330 Subject: [PATCH 3/4] refactor(argocd): handle null value when subsets of argo application are unchecked --- .../features/terraform/components/Argocd.tsx | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/web/src/features/terraform/components/Argocd.tsx b/web/src/features/terraform/components/Argocd.tsx index 2225d9a5..049a5707 100644 --- a/web/src/features/terraform/components/Argocd.tsx +++ b/web/src/features/terraform/components/Argocd.tsx @@ -21,7 +21,7 @@ const Argocd = () => { const [error, setError] = useState(false); - const {data, isPending, isError, isSuccess, mutate} = useMutation({ + const {data, isPending, isError, isSuccess, mutate} = useMutation({ mutationKey: ["argocd"], mutationFn: async ({body}) => { await apiClient.post(Endpoints.POST_IAC_ARGOCD, {...body}); @@ -43,7 +43,7 @@ const Argocd = () => { const blob = new Blob([data.data], { type: data.headers['content-type'] }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); - link.download = "Argpcd.zip"; + link.download = "Argocd.zip"; document.body.appendChild(link); link.click(); document.body.removeChild(link); @@ -60,15 +60,19 @@ const Argocd = () => { const handleSubmit = async () => { const body = { - "argocd_application": { - "sync_policy": { - "auto_prune": buttons.auto_prune, - "self_heal": buttons.self_heal - } - }, - "argocd_repository": buttons.argocd_repository, - "application_depends_repository": buttons.application_depends_repository - } + argocd_application: ( + buttons.auto_prune || buttons.self_heal + ? { + sync_policy: { + auto_prune: buttons.auto_prune, + self_heal: buttons.self_heal + } + } + : null + ), + argocd_repository: buttons.argocd_repository, + application_depends_repository: buttons.application_depends_repository + }; mutate({body}) } From 31378f5010e376f23ef59ebe0492dfcc8f737e1e Mon Sep 17 00:00:00 2001 From: Milad Sadeghi Date: Wed, 27 Nov 2024 13:43:47 +0330 Subject: [PATCH 4/4] refactor(argocd): add input trigger to control ArgoCD application menu - Updated logic to send null when the input trigger is set to false. --- .../features/terraform/components/Argocd.tsx | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/web/src/features/terraform/components/Argocd.tsx b/web/src/features/terraform/components/Argocd.tsx index 049a5707..49d55457 100644 --- a/web/src/features/terraform/components/Argocd.tsx +++ b/web/src/features/terraform/components/Argocd.tsx @@ -15,8 +15,8 @@ const Argocd = () => { }); const [menus, setMenus] = useState({ - argocd_application: false, - sync_policy: false + argocd_application: true, + sync_policy: true }); const [error, setError] = useState(false); @@ -50,6 +50,16 @@ const Argocd = () => { } }, [isSuccess, data]) + useEffect(() => { + if (!menus.argocd_application) { + setButtons({ + ...buttons, + auto_prune: false, + self_heal: false + }) + } + }, [menus]) + const handleButtons = (button: keyof typeof buttons) => { setButtons({...buttons, [button]: !buttons[button]}) } @@ -61,7 +71,7 @@ const Argocd = () => { const handleSubmit = async () => { const body = { argocd_application: ( - buttons.auto_prune || buttons.self_heal + menus.argocd_application ? { sync_policy: { auto_prune: buttons.auto_prune, @@ -81,22 +91,24 @@ const Argocd = () => {
- -
+ {handleMenus("argocd_application")}} /> +
+
-
+

Sync Policy

+ + +

Auto Prune

handleButtons("auto_prune")} /> + }) } checked={buttons.auto_prune} onChange={() => handleButtons("auto_prune")} />
@@ -104,7 +116,7 @@ const Argocd = () => {

Self Heal

handleButtons("self_heal")} /> + }) } checked={buttons.self_heal} onChange={() => handleButtons("self_heal")} />
@@ -114,7 +126,7 @@ const Argocd = () => {

Argocd Repository

handleButtons("argocd_repository")} /> + }) } checked={buttons.argocd_repository} onChange={() => handleButtons("argocd_repository")} />
@@ -122,12 +134,12 @@ const Argocd = () => {

Application Depends Repository

handleButtons("application_depends_repository")} /> + }) } checked={buttons.application_depends_repository} onChange={() => handleButtons("application_depends_repository")} />
- + ); };