From a45c9bbfaa22ab7fe6cbc32b00135fce5974cdb5 Mon Sep 17 00:00:00 2001 From: aditya-tiwari-zs Date: Fri, 13 Dec 2024 13:31:18 +0530 Subject: [PATCH 1/3] added the application view and add feature --- Queries/Application/index.js | 22 ++++ components/Cards/applicationCard.js | 118 +++++++++++++++++++ components/Input/inputWithButton.js | 67 +++++++++++ hooks/application/addApplication.js | 41 +++++++ hooks/application/getApplicationList.js | 27 +++++ hooks/cloudAccount/addCloudAccount.js | 2 +- partials/Header/index.js | 58 ++++------ partials/application/createForm.js | 148 ++++++++++++++++++++++++ partials/draggableList/index.js | 84 ++++++++++++++ src/app/applications/create/page.js | 34 ++++++ src/app/applications/page.js | 67 +++++++++++ src/app/cloud-accounts/create/page.js | 38 +++--- src/app/cloud-accounts/page.js | 2 +- src/app/globals.css | 6 + src/app/layout.js | 9 +- svg/application.js | 46 ++++++++ svg/configDiff.js | 41 +++++++ svg/deploymentSpace.js | 81 +++++++++++++ utlis/apiEndpoints.js | 1 + 19 files changed, 833 insertions(+), 59 deletions(-) create mode 100644 Queries/Application/index.js create mode 100644 components/Cards/applicationCard.js create mode 100644 components/Input/inputWithButton.js create mode 100644 hooks/application/addApplication.js create mode 100644 hooks/application/getApplicationList.js create mode 100644 partials/application/createForm.js create mode 100644 partials/draggableList/index.js create mode 100644 src/app/applications/create/page.js create mode 100644 src/app/applications/page.js create mode 100644 svg/application.js create mode 100644 svg/configDiff.js create mode 100644 svg/deploymentSpace.js diff --git a/Queries/Application/index.js b/Queries/Application/index.js new file mode 100644 index 0000000..3480432 --- /dev/null +++ b/Queries/Application/index.js @@ -0,0 +1,22 @@ +import apiClient from '../../utlis/apiClient'; +import { APPLICATION_ENDPOINT } from '../../utlis/apiEndpoints'; + +const url = APPLICATION_ENDPOINT; + +export const getApplication = async () => { + try { + const data = await apiClient.get(url); + return data; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to fetch applications'); + } +}; + +export const addApplication = async (values) => { + try { + const response = await apiClient.post(url, values); + return response; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to add application'); + } +}; diff --git a/components/Cards/applicationCard.js b/components/Cards/applicationCard.js new file mode 100644 index 0000000..10a0bbf --- /dev/null +++ b/components/Cards/applicationCard.js @@ -0,0 +1,118 @@ +import React, { useEffect } from 'react'; +import { useRouter } from 'next/navigation'; +import { formatTime } from '../../utlis/helperFunc'; + +const ApplicationCard = ({ + data, + view, + // handleLogsOpen, + // handleRetryStatus, + // putToRetryNameSpace, +}) => { + const router = useRouter(); + + useEffect(() => { + if (data?.id) { + router.prefetch(`/applications/${data?.id}`); + } + }, [data?.id, router]); + + // const handleRetry = (e, id) => { + // e.stopPropagation(); + // handleRetryStatus(id); + // }; + + // const latestLog = (e, data) => { + // e.stopPropagation(); + // handleLogsOpen(data); + // }; + + const handleRedirect = () => { + router.push(`/applications/${data?.id}/deploymentSpace`); + }; + + return ( +
+
+

{data?.name}

+ + {/* {view !== 'navBar' && ( +
+ + {REFRESH_STATUS?.includes(data?.status) ? ( + + ) : ( + + )} + +
+ )} */} + + {/* {data?.retry && ( +
+ handleRetry(e, data?.id)} + > + + +
+ )} */} + + {/* {view !== 'navBar' && REFRESH_STATUS?.includes(data?.status) && ( +
+ + latestLog(e, data)}> + + + +
+ )} */} +
+ +
+ {/*
+

{data?.providerName}

+

 Cluster

+
*/} + +
+ {data?.services !== 0 && ( +

{data?.environments?.length}

+ )} +

+   + {data?.environments?.length === 1 + ? 'Environment' + : data?.environments?.length > 1 + ? 'Environments' + : ' '} +

+
+
+ +
+ Updated At{' '} + {/* + {item?.updatedByIdentifier || item?.createdByIdentifier} + */} + {formatTime(data?.updatedAt)} +
+ {/*

+ {formatTime(data?.updatedAt)} +

*/} +
+ ); +}; + +export default ApplicationCard; diff --git a/components/Input/inputWithButton.js b/components/Input/inputWithButton.js new file mode 100644 index 0000000..0948192 --- /dev/null +++ b/components/Input/inputWithButton.js @@ -0,0 +1,67 @@ +import { PlusCircleIcon } from '@heroicons/react/16/solid'; +import { useEffect, useState } from 'react'; + +export default function InputWithButton({ + type = '', + value, + error = false, + name, + required, + testExp, + helperText, + helperTextClass, + errorTextClass, + errorText, + onChange, + onClick, + inputProps, + className, + ...props +}) { + const [internalError, setInternalError] = useState(error); + useEffect(() => { + setInternalError(error); + }, [error]); + return ( +
+
+
+ { + if (testExp && !new RegExp(testExp).test(e.target.value)) { + setInternalError(true); + } else { + setInternalError(error); + } + if (onChange) onChange(e); + }} + /> + {/*
+ +
+ {helperText != null && ( +

{helperText}  

+ )} + {internalError && errorText && ( +

{errorText}  

+ )} +
+ ); +} diff --git a/hooks/application/addApplication.js b/hooks/application/addApplication.js new file mode 100644 index 0000000..c698fa0 --- /dev/null +++ b/hooks/application/addApplication.js @@ -0,0 +1,41 @@ +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { addApplication } from '../../Queries/Application'; + +// import { addApplication } from '../../Queries/Application'; + +const useAddApplication = () => { + const router = useRouter(); + const [values, setValues] = useState({ + name: '', + environments: [], + }); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + const handleSubmit = async (values) => { + setIsLoading(true); + setError(null); + + try { + const data = await addApplication(values); + setError(null); + router.push('/applications'); + return data; + } catch (error) { + setError(error.message); + } finally { + setIsLoading(false); + } + }; + + return { + values, + setValues, + handleSubmit, + isLoading, + error, + }; +}; + +export default useAddApplication; diff --git a/hooks/application/getApplicationList.js b/hooks/application/getApplicationList.js new file mode 100644 index 0000000..b8062cb --- /dev/null +++ b/hooks/application/getApplicationList.js @@ -0,0 +1,27 @@ +import { useState, useEffect } from 'react'; +import { getApplication } from '../../Queries/Application'; + +const useApplicationList = () => { + const [applications, setApplications] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchCloudAccounts = async () => { + try { + const data = await getApplication(); + setApplications(data); + } catch (error) { + setError(error.message); + } finally { + setLoading(false); + } + }; + + fetchCloudAccounts(); + }, []); + + return { applications, loading, error }; +}; + +export default useApplicationList; diff --git a/hooks/cloudAccount/addCloudAccount.js b/hooks/cloudAccount/addCloudAccount.js index 98b9420..5942cc4 100644 --- a/hooks/cloudAccount/addCloudAccount.js +++ b/hooks/cloudAccount/addCloudAccount.js @@ -21,7 +21,7 @@ const formateData = async (values, provider) => { const finalValues = { // orgId: tokenInfo?.["tenant-id"], name: values.name, - provider: provider, + provider, // configs: { // name: // provider !== "aws" diff --git a/partials/Header/index.js b/partials/Header/index.js index 9de962e..a132c4a 100644 --- a/partials/Header/index.js +++ b/partials/Header/index.js @@ -1,14 +1,15 @@ 'use client'; -import { Fragment, useState } from 'react'; +import { Fragment, useEffect, useState } from 'react'; import { Disclosure, Transition, Dialog } from '@headlessui/react'; import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'; import AppLogo from '../../svg/appLogo'; import Image from 'next/image'; import Link from 'next/link'; -import { useRouter } from 'next/navigation'; +import { usePathname, useRouter } from 'next/navigation'; import CloudAccountSVG from '../../svg/cloudAccount'; import APP_LOGO_IMAGE from '../../public/applogoWithText.svg'; +import ApplicationSvg from '../../svg/application'; function classNames(...classes) { return classes.filter(Boolean).join(' '); @@ -16,34 +17,21 @@ function classNames(...classes) { export function TopBar() { const router = useRouter(); - const [tab] = useState(0); + const pathname = usePathname(); + const [tab, setTab] = useState(); const [sidebarOpen, setSidebarOpen] = useState(false); - // useEffect(() => { - // let DashboardView = navigation - // .map(function (ele) { - // return `/${ele.routeName.split("/")[1]}`; - // }) - // .indexOf(`/${router.asPath.split("/")[1]}`); + useEffect(() => { + let DashboardView = navigation + .map(function (ele) { + return `/${ele.routeName.split('/')[1]}`; + }) + .indexOf(`/${pathname.split('/')[1]}`); - // DashboardView = DashboardView !== -1 ? DashboardView || 0 : -1; - // if ( - // DashboardView === -1 && - // `${router.asPath.split("/")[1]}` === "table" && - // router?.query?.["cloud-account-id"] === undefined - // ) { - // setTab(0); - // } else if ( - // DashboardView === -1 && - // `${router.asPath.split("/")[1]}` === "table" && - // router?.query?.["cloud-account-id"] && - // router?.query?.["application-id"] === undefined - // ) { - // setTab(1); - // } else { - // setTab(DashboardView); - // } - // }, [router.route]); + DashboardView = DashboardView !== -1 ? DashboardView || 0 : -1; + + setTab(DashboardView); + }, [pathname]); // const handleAppData = (entity, values) => // setAppData((prevValues) => ({ ...prevValues, [entity]: values })); @@ -118,14 +106,14 @@ export function TopBar() { isLoading: false, icon: CloudAccountSVG, }, - // { - // name: "Applications", - // routeName: "/applications", - // hover: true, - // selected: true, - // isLoading: false, - // icon: ApplicationSvg, - // }, + { + name: 'Applications', + routeName: '/applications', + hover: true, + selected: true, + isLoading: false, + icon: ApplicationSvg, + }, // { // name: "Observability", // routeName: diff --git a/partials/application/createForm.js b/partials/application/createForm.js new file mode 100644 index 0000000..f32128a --- /dev/null +++ b/partials/application/createForm.js @@ -0,0 +1,148 @@ +'use client'; + +import React, { useState } from 'react'; +import Label from '../../components/Label'; +import Input from '../../components/Input'; +import InputWithButton from '../../components/Input/inputWithButton'; +import DraggableList from '../draggableList'; +import Button from '../../components/Button'; +import useAddApplication from '../../hooks/application/addApplication'; +import ErrorComponent from '../../components/ErrorComponent'; + +const CreateAppForm = () => { + const { values, setValues, handleSubmit, isLoading, error } = useAddApplication(); + + const [chips, setChips] = useState([ + { name: 'prod', selected: false }, + { name: 'stage', selected: false }, + { name: 'test', selected: false }, + ]); + const [inputValue, setInputValue] = useState(''); + + // Handle input change + const handleInputChange = (e) => setInputValue(e.target.value); + + // Add new chip + const handleAddChip = () => { + const chipName = inputValue.trim(); + if (chipName && !chips.some((chip) => chip.name === chipName)) { + setChips([...chips, { name: chipName, selected: true }]); + setInputValue(''); // Clear input + } + }; + + // Toggle chip selection + const toggleChipSelection = (name) => { + setChips((prevChips) => + prevChips.map((chip) => (chip.name === name ? { ...chip, selected: !chip.selected } : chip)), + ); + }; + + const handleChange = (e) => { + setValues({ ...values, [e.target.name]: e.target.value }); + }; + + const handleListUpdate = (updatedList) => { + setValues({ ...values, environments: updatedList }); + }; + + const handleOnSubmit = (e) => { + e.preventDefault(); + handleSubmit(values); + }; + + return ( +
+
+
+ +
+ } + // inputProps={provider === 'azure' ? { minLength: 6 } : { maxLength: 16, minLength: 6 }} + /> +
+
+
+ +
+ } + // inputProps={provider === 'azure' ? { minLength: 6 } : { maxLength: 16, minLength: 6 }} + /> +
+
+
+ +
+ {chips.map((chip) => ( + toggleChipSelection(chip.name)} + className={`inline-flex items-center rounded-full px-3 py-2 text-xs font-medium cursor-pointer hover:bg-gray-200 ${ + chip.selected + ? 'bg-primary-100 text-primary-600 hover:!bg-primary-200 ' + : 'bg-gray-100 text-gray-600' + }`} + > + {chip.name} + + ))} +
+
+ +
+ {values?.environments?.length > 0 && ( + + )} + + {values?.environments?.length > 0 && ( +

+ *Rearrange the order by drag and drop +

+ )} +
+
+ +
+
+ {error && ( + + )} +
+
+ +
+ +
+
+ ); +}; + +export default CreateAppForm; diff --git a/partials/draggableList/index.js b/partials/draggableList/index.js new file mode 100644 index 0000000..f478ec7 --- /dev/null +++ b/partials/draggableList/index.js @@ -0,0 +1,84 @@ +import { ChevronRightIcon } from '@heroicons/react/20/solid'; +import { useEffect, useRef, useState } from 'react'; + +export default function DraggableList({ chips, handleListUpdate }) { + const [pages, setPages] = useState([]); + + useEffect(() => { + const updatedList = chips + ?.filter((item) => item.selected) + .map((item, index) => ({ ...item, level: index + 1 })); + setPages(updatedList); + handleListUpdate(updatedList); + }, [chips]); + + const draggingItem = useRef(); + const dragOverItem = useRef(); + + const handleDragStart = (e, position) => { + draggingItem.current = position; + }; + + // const handleDragEnter = (e, position) => { + // dragOverItem.current = position; + // }; + + const handleDragEnter = (e, position) => { + dragOverItem.current = position; + const listCopy = [...pages]; + const draggingItemContent = listCopy[draggingItem.current]; + listCopy.splice(draggingItem.current, 1); + listCopy.splice(dragOverItem.current, 0, draggingItemContent); + + // Update orders based on the new position + const updatedList = listCopy.map((item, index) => ({ + ...item, + order: index + 1, // Update the `order` based on position + })); + + draggingItem.current = dragOverItem.current; + dragOverItem.current = null; + setPages(updatedList); + handleListUpdate(updatedList); + }; + + // const handleDragEnd = () => { + // const listCopy = [...pages]; + // const draggingItemContent = listCopy[draggingItem.current]; + // listCopy.splice(draggingItem.current, 1); + // listCopy.splice(dragOverItem.current, 0, draggingItemContent); + + // draggingItem.current = null; + // dragOverItem.current = null; + // setPages(listCopy); + // }; + + return ( + + ); +} diff --git a/src/app/applications/create/page.js b/src/app/applications/create/page.js new file mode 100644 index 0000000..1d0d975 --- /dev/null +++ b/src/app/applications/create/page.js @@ -0,0 +1,34 @@ +import Head from 'next/head'; +import React from 'react'; +import CreateAppForm from '../../../../partials/application/createForm'; + +const CreateApplication = () => { + return ( + <> + + Create Application + +
+
+
+
+
+

{`Add Application`}

+

+ Add application details by either selecting an empty project to create a fresh, + blank application or choosing from our predefined templates, which come + pre-configured with the essential setup for your app. +

+
+
+ +
+
+
+
+
+ + ); +}; + +export default CreateApplication; diff --git a/src/app/applications/page.js b/src/app/applications/page.js new file mode 100644 index 0000000..0017784 --- /dev/null +++ b/src/app/applications/page.js @@ -0,0 +1,67 @@ +'use client'; + +import React from 'react'; +import HeadingComponent from '../../../components/HeaderComponents'; +import { PlusCircleIcon } from '@heroicons/react/20/solid'; +import CloudAccountCard from '../../../components/Cards/cloudAccountCard'; +import EmptyComponent from '../../../components/EmptyPageComponent'; +import BlankCloudAccountSvg from '../../../svg/emptyCloudAccount'; +import Link from 'next/link'; +import CompleteLoader from '../../../components/Loaders/CompletePageLoader'; +import ErrorComponent from '../../../components/ErrorComponent'; +import useApplicationList from '../../../hooks/application/getApplicationList'; +import ApplicationCard from '../../../components/Cards/applicationCard'; + +const CloudAccounts = () => { + const { applications, loading, error } = useApplicationList(); + + return ( +
+ {applications?.data?.length > 0 && ( + + + + } + /> + )} + + {loading && } +
+ {applications?.data?.map((item, idx) => { + return ( + + ); + })} +
+ + {applications?.data?.length === 0 && ( + } + redirectLink={'/applications/create'} + buttonTitle={'Add Application'} + title={'Please start by setting up your first application'} + /> + )} + + {error && } +
+ ); +}; + +export default CloudAccounts; diff --git a/src/app/cloud-accounts/create/page.js b/src/app/cloud-accounts/create/page.js index d78875d..c169ead 100644 --- a/src/app/cloud-accounts/create/page.js +++ b/src/app/cloud-accounts/create/page.js @@ -31,24 +31,26 @@ const CreateCloud = () => { }; return ( -
-
-
-
-

Add Cloud Account

-

- To add a cloud account, provide your Service Account Key JSON and assign a meaningful - name to identify your cloud account easily. -

-
-
- +
+
+
+
+
+

Add Cloud Account

+

+ To add a cloud account, provide your Service Account Key JSON and assign a + meaningful name to identify your cloud account easily. +

+
+
+ +
diff --git a/src/app/cloud-accounts/page.js b/src/app/cloud-accounts/page.js index 29a14e3..7a5743e 100644 --- a/src/app/cloud-accounts/page.js +++ b/src/app/cloud-accounts/page.js @@ -15,7 +15,7 @@ const CloudAccounts = () => { const { cloudAccounts, loading, error } = useCloudAccountList(); return ( -
+
{cloudAccounts?.data?.length > 0 && ( - }> - -
{children}
-
+ {/* }> */} + + {/*
{children}
*/} +
{children}
+ {/*
*/} ); diff --git a/svg/application.js b/svg/application.js new file mode 100644 index 0000000..924b91e --- /dev/null +++ b/svg/application.js @@ -0,0 +1,46 @@ +import React from 'react'; + +const ApplicationSvg = ({ color = '#9197B3' }) => { + return ( + + + + + + + + + + + + + + + + ); +}; + +export default ApplicationSvg; diff --git a/svg/configDiff.js b/svg/configDiff.js new file mode 100644 index 0000000..dfc587b --- /dev/null +++ b/svg/configDiff.js @@ -0,0 +1,41 @@ +import React from 'react'; + +export const ConfigDiff = ({ color = '#9197B3', ...rest }) => { + return ( + + + + + + + + + + + + ); +}; diff --git a/svg/deploymentSpace.js b/svg/deploymentSpace.js new file mode 100644 index 0000000..95e447f --- /dev/null +++ b/svg/deploymentSpace.js @@ -0,0 +1,81 @@ +import React from 'react'; + +export const DeploymentSpace = ({ color = '#9197B3', ...rest }) => { + return ( + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/utlis/apiEndpoints.js b/utlis/apiEndpoints.js index 74b0ac8..fc9cfc5 100644 --- a/utlis/apiEndpoints.js +++ b/utlis/apiEndpoints.js @@ -1 +1,2 @@ export const CLOUD_ACCOUNT_ENDPOINT = '/cloud-accounts'; +export const APPLICATION_ENDPOINT = '/applications'; From 84b9ec0389f520e8f114e79edb9c614058c4ab5d Mon Sep 17 00:00:00 2001 From: aditya-tiwari-zs Date: Tue, 17 Dec 2024 18:08:02 +0530 Subject: [PATCH 2/3] add the config diff and deployment space feature --- Queries/Application/index.js | 20 + Queries/DeploymentSpace/index.js | 14 + components/Animation/animateHeight.js | 57 +++ components/BreadCrumb/index.js | 42 +++ components/Cards/applicationCard.js | 102 ++--- components/Cards/cloudAccountCard.js | 111 +----- components/JsonTable/index.js | 348 ++++++++++++++++++ components/Loaders/LinearLoader/index.js | 19 + components/Table/table.js | 28 ++ components/Table/tableBody.js | 39 ++ components/Table/tableHeader.js | 26 ++ hooks/Header/addHeader.js | 37 ++ hooks/application/getApplicationList.js | 4 +- hooks/deploymentSpace/addDeploymentSpace.js | 37 ++ hooks/deploymentSpace/getDeploymentSpace.js | 108 ++++++ hooks/environment/addEnvironment.js | 37 ++ libs/context.js | 18 + partials/Environment/addForm.js | 65 ++++ partials/Header/index.js | 6 +- partials/cloudAccount/createForm.js | 2 +- partials/draggableList/index.js | 29 +- partials/nestedList/deploymentConfig.js | 48 +++ partials/nestedList/index.js | 248 +++++++++++++ partials/sideBar/index.js | 335 +++++++++++++++++ .../[application-id]/configDiff/page.js | 78 ++++ .../configureDeploymentSpace/page.js | 59 +++ .../deploymentSpace/addEnvironment/page.js | 58 +++ .../[application-id]/deploymentSpace/page.js | 114 ++++++ .../applications/[application-id]/layout.js | 17 + src/app/applications/create/page.js | 5 +- src/app/applications/page.js | 1 - src/app/cloud-accounts/page.js | 11 +- src/app/globals.css | 10 + src/app/layout.js | 13 +- utlis/apiClient.js | 40 +- utlis/apiEndpoints.js | 1 + 36 files changed, 1943 insertions(+), 244 deletions(-) create mode 100644 Queries/DeploymentSpace/index.js create mode 100644 components/Animation/animateHeight.js create mode 100644 components/BreadCrumb/index.js create mode 100644 components/JsonTable/index.js create mode 100644 components/Loaders/LinearLoader/index.js create mode 100644 components/Table/table.js create mode 100644 components/Table/tableBody.js create mode 100644 components/Table/tableHeader.js create mode 100644 hooks/Header/addHeader.js create mode 100644 hooks/deploymentSpace/addDeploymentSpace.js create mode 100644 hooks/deploymentSpace/getDeploymentSpace.js create mode 100644 hooks/environment/addEnvironment.js create mode 100644 libs/context.js create mode 100644 partials/Environment/addForm.js create mode 100644 partials/nestedList/deploymentConfig.js create mode 100644 partials/nestedList/index.js create mode 100644 partials/sideBar/index.js create mode 100644 src/app/applications/[application-id]/configDiff/page.js create mode 100644 src/app/applications/[application-id]/deploymentSpace/[environment-id]/configureDeploymentSpace/page.js create mode 100644 src/app/applications/[application-id]/deploymentSpace/addEnvironment/page.js create mode 100644 src/app/applications/[application-id]/deploymentSpace/page.js create mode 100644 src/app/applications/[application-id]/layout.js diff --git a/Queries/Application/index.js b/Queries/Application/index.js index 3480432..23635a4 100644 --- a/Queries/Application/index.js +++ b/Queries/Application/index.js @@ -12,6 +12,26 @@ export const getApplication = async () => { } }; +export const addEnvironment = async (id, values) => { + const updatedUrl = `${url}/${id}/environments`; + try { + const response = await apiClient.post(updatedUrl, values); + return response; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to add environment'); + } +}; + +export const getApplicationById = async (id) => { + const updatedUrl = `${url}/${id}`; + try { + const response = await apiClient.get(updatedUrl); + return response; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to fetch application'); + } +}; + export const addApplication = async (values) => { try { const response = await apiClient.post(url, values); diff --git a/Queries/DeploymentSpace/index.js b/Queries/DeploymentSpace/index.js new file mode 100644 index 0000000..4de7696 --- /dev/null +++ b/Queries/DeploymentSpace/index.js @@ -0,0 +1,14 @@ +import apiClient from '../../utlis/apiClient'; +import { ENVIRONMENT_ENDPOINT } from '../../utlis/apiEndpoints'; + +const url = ENVIRONMENT_ENDPOINT; + +export const addDeploymentConfig = async (id, values) => { + const updatedUrl = `${url}/${id}/deploymentspace`; + try { + const response = await apiClient.post(updatedUrl, values); + return response; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to add deployment space'); + } +}; diff --git a/components/Animation/animateHeight.js b/components/Animation/animateHeight.js new file mode 100644 index 0000000..953e25a --- /dev/null +++ b/components/Animation/animateHeight.js @@ -0,0 +1,57 @@ +import React, { useEffect, useRef, useState } from 'react'; + +const AnimateHeight = ({ children, className, dependency, customAnimation }) => { + const [height, setHeight] = useState(0); + const parentRef = useRef(null); + + let time; + const handleChange = () => { + if (!parentRef.current) { + return; + } + const childFirst = parentRef.current.children[0]; + const heightChild = childFirst.getBoundingClientRect().height; + setHeight(heightChild); + time = setTimeout(() => { + if (heightChild !== childFirst.getBoundingClientRect().height) { + handleChange(); + } + }, 100); + }; + + useEffect(() => { + handleChange(); + return () => { + clearTimeout(time); + }; + }, [children, dependency]); + + useEffect(() => { + if (!parentRef.current) { + return; + } + const resizeOb = new ResizeObserver(handleChange); + const childFirst = parentRef.current.children[0]; + if (childFirst) resizeOb.observe(childFirst); + + return () => { + if (childFirst) resizeOb.unobserve(childFirst); + }; + }, [parentRef.current]); + + return ( +
+
+
{children}
+
+
+ ); +}; + +export default AnimateHeight; diff --git a/components/BreadCrumb/index.js b/components/BreadCrumb/index.js new file mode 100644 index 0000000..2efaf6c --- /dev/null +++ b/components/BreadCrumb/index.js @@ -0,0 +1,42 @@ +import { ChevronRightIcon } from '@heroicons/react/20/solid'; +import Link from 'next/link'; + +export default function BreadCrumbComp({ breadcrumbList, style }) { + return ( + <> + {breadcrumbList[1]?.name !== 'loading...' && ( + + )} + + ); +} diff --git a/components/Cards/applicationCard.js b/components/Cards/applicationCard.js index 10a0bbf..c7a14e0 100644 --- a/components/Cards/applicationCard.js +++ b/components/Cards/applicationCard.js @@ -2,13 +2,7 @@ import React, { useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { formatTime } from '../../utlis/helperFunc'; -const ApplicationCard = ({ - data, - view, - // handleLogsOpen, - // handleRetryStatus, - // putToRetryNameSpace, -}) => { +const ApplicationCard = ({ data, view }) => { const router = useRouter(); useEffect(() => { @@ -17,76 +11,23 @@ const ApplicationCard = ({ } }, [data?.id, router]); - // const handleRetry = (e, id) => { - // e.stopPropagation(); - // handleRetryStatus(id); - // }; - - // const latestLog = (e, data) => { - // e.stopPropagation(); - // handleLogsOpen(data); - // }; - const handleRedirect = () => { router.push(`/applications/${data?.id}/deploymentSpace`); }; return (

{data?.name}

- - {/* {view !== 'navBar' && ( -
- - {REFRESH_STATUS?.includes(data?.status) ? ( - - ) : ( - - )} - -
- )} */} - - {/* {data?.retry && ( -
- handleRetry(e, data?.id)} - > - - -
- )} */} - - {/* {view !== 'navBar' && REFRESH_STATUS?.includes(data?.status) && ( -
- - latestLog(e, data)}> - - - -
- )} */}
- {/*
-

{data?.providerName}

-

 Cluster

-
*/} - -
+
{data?.services !== 0 && (

{data?.environments?.length}

)} @@ -101,7 +42,42 @@ const ApplicationCard = ({
-
+ {data?.environments?.length > 1 ? ( +
+ {data.environments.map((single) => ( +
+
+

Environment

+

{single.name}

+
+ +
+
+

Order

+
+

{single.level ?? '1'}

+
+
+ ))} +
+ ) : ( +
+
+

Environment

+

{data?.environments?.[0]?.name}

+
+ +
+

Order

+

{data?.environments?.[0]?.level}

+
+
+ )} + +
Updated At{' '} {/* {item?.updatedByIdentifier || item?.createdByIdentifier} diff --git a/components/Cards/cloudAccountCard.js b/components/Cards/cloudAccountCard.js index 535fade..9f9b005 100644 --- a/components/Cards/cloudAccountCard.js +++ b/components/Cards/cloudAccountCard.js @@ -1,127 +1,24 @@ import React from 'react'; import { formatTime } from '../../utlis/helperFunc'; -import { - PROVIDER_ICON_MAPPER, - // REFRESH_STATUS, - // colourCode, -} from '../../constant'; -// import DotWithProgress from "../Loader/DotWithProgression"; -// import { LogSvg } from "../../svg/sidebar/logs"; -// import IconButton from "../FormComponent/IconButton"; -// import RefreshIcon from "../../svg/refresh"; -// import Tooltip from "../FormComponent/toolTip"; -// import { useRouter } from "next/router"; - -const CloudAccountCard = ({ - item, - view, - // handleLogsOpen, - // handleRetryStatus, - // putToProvider, -}) => { - // const router = useRouter(); - - // useEffect(() => { - // if (item?.id) { - // router.prefetch(`/cloud-accounts/${item.id}/infrastructure`); - // } - // }, [item?.id, router]); - - // const handleRetry = (e, id) => { - // e.stopPropagation(); - // handleRetryStatus(id); - // }; - - // const latestLog = (e, data) => { - // e.stopPropagation(); - // handleLogsOpen(data); - // }; - - // const handleRedirect = () => { - // router.push(`/cloud-accounts/${item?.id}/infrastructure`); - // }; +import { PROVIDER_ICON_MAPPER } from '../../constant'; +const CloudAccountCard = ({ item }) => { return (
{PROVIDER_ICON_MAPPER?.[item?.provider]} {item?.name} - {/* {view === "cloudAccount" && ( - -
- {REFRESH_STATUS?.includes(item?.status) ? ( - - ) : ( - <> - - - )} -
-
- )} */} - {/* {item?.retry && ( - -
- handleRetry(e, item?.id)} - > - - -
-
- )} */} - {/* {view === "cloudAccount" && - REFRESH_STATUS?.includes(item?.status) && - item?.clusterId?.defaultId && ( -
- - latestLog(e, item)}> - - - -
- )} */}
{item?.providerId} - {/* - - {item?.svcGroup === 1 - ? `1 App` - : item?.svcGroup > 1 - ? `${item?.svcGroup} Apps` - : ""} - - */}
- Updated At{' '} - {/* - {item?.updatedByIdentifier || item?.createdByIdentifier} - */} - {formatTime(item?.updatedAt)} + Updated At {formatTime(item?.updatedAt)}
- - {/*
- {formatTime(item?.updatedAt)} -
*/}
); }; diff --git a/components/JsonTable/index.js b/components/JsonTable/index.js new file mode 100644 index 0000000..0bc0d12 --- /dev/null +++ b/components/JsonTable/index.js @@ -0,0 +1,348 @@ +import React, { useEffect, useRef } from 'react'; + +const INITIAL_CELL_STYLES = { + minWidth: 500, + maxWidth: 500, + wordBreak: 'break-all', + marginRight: 40, + padding: 10, + paddingBottom: 10, +}; + +const INITIAL_KEY_CELL_STYLES = { + minWidth: 200, + maxWidth: 200, + wordBreak: 'break-all', + marginRight: 40, + paddingTop: 10, +}; + +const COLOURS = { + BORDER: '#e5e7eb', +}; + +const JsonComparisonTable = ({ + data, + keysStyles = {}, + screenOffsetTop, + isKeysVisible = false, + difference = false, + isKeysScrollable = false, + isAlternateColStyle = true, + isAlternateRowStyle = true, +}) => { + const dataTableRef = useRef(null); + const dataHeaderRef = useRef(null); + const parentRef = useRef(null); + const keyRefs = useRef([]); + const cellRefs = useRef([]); + + useEffect(() => { + const handleScrollBody = (e) => { + if (dataHeaderRef.current) { + dataHeaderRef.current.scrollLeft = e.target.scrollLeft; + } + }; + + const handleScrollHeader = (e) => { + if (dataHeaderRef.current) { + dataTableRef.current.scrollLeft = e.target.scrollLeft; + } + }; + + if (dataTableRef.current) { + dataTableRef.current.addEventListener('scroll', handleScrollBody); + dataHeaderRef.current.addEventListener('scroll', handleScrollHeader); + } + + return () => { + if (dataTableRef.current) { + dataTableRef.current.removeEventListener('scroll', handleScrollBody); + dataHeaderRef.current.removeEventListener('scroll', handleScrollHeader); + } + }; + }, [dataTableRef]); + + useEffect(() => { + const resizeObserver = new ResizeObserver((entries) => { + for (const entry of entries) { + const height = entry.contentRect.height; + const index = Number(entry.target.getAttribute('data-index')); + const keyElement = keyRefs.current[index]; + const maxHeight = Math.max(height, keyElement?.clientHeight); + if (keyElement) { + // Check if keyElement exists before accessing its style + keyElement.style.height = `${maxHeight}px`; + } + } + }); + + keyRefs.current.forEach((keyRef, index) => { + let maxHeight = 0; + if (keyRef && cellRefs.current[index]) { + for (const elements of cellRefs.current[index]) { + resizeObserver.observe(elements); + if (elements.clientHeight > maxHeight) { + maxHeight = Math.max(maxHeight, elements.clientHeight); + } + } + } + + keyRef.style.height = `${maxHeight}px`; + }); + + return () => { + resizeObserver.disconnect(); + }; + }, [data]); + + const getColumStyles = (dataColumn) => { + let styles = { ...INITIAL_CELL_STYLES }; + + if (dataColumn) { + styles = { + ...styles, + ...(dataColumn.styles ?? {}), + }; + } + + return styles; + }; + + const keys = new Set(); + + for (const columns of data) { + for (const key in columns.data) { + keys.add(key); + } + } + + const keysOfData = Array.from(keys); + + const keyCellStyles = { ...INITIAL_KEY_CELL_STYLES, ...keysStyles }; + + const getFormattedCell = (dataCell) => { + if (typeof dataCell === 'string' || typeof dataCell === 'number') { + return dataCell; + } + + if (typeof dataCell === 'function') { + return dataCell(); + } + + if (typeof dataCell === 'object') { + return JSON.stringify(dataCell); + } + + return ''; + }; + + const isKeyDifferent = (key, value) => { + if (data.length === 0) return null; + if (!value) return null; + if (typeof value !== 'string') return null; + + for (let i = 1; i < data.length; i++) { + if (data[i].data[key] && data[i].data[key] !== value) { + return true; + } + } + + return false; + }; + + return ( +
+
+
+ {isKeysVisible && ( +
+ Key +
+ )} +
+ {data.map((single, i) => ( +
+
+ {single.label} +
+
+ ))} +
+
+ +
+ {isKeysVisible && ( +
+ {keysOfData.map((key, iRow) => ( +
(keyRefs.current[iRow] = el)} + > + + {key} + +
+ ))} +
+ )} + +
+ {keysOfData.map((key, iRow) => ( +
+ {data.map((single, i) => ( +
{ + if (cellRefs.current[iRow]) { + cellRefs.current[iRow][i] = el; + } else { + cellRefs.current[iRow] = [el]; + } + }} + > +
+ {difference && isKeyDifferent(key, single.data[key]) === true && ( +
+ +
+ )} + {getFormattedCell(single.data[key])} +
+
+ ))} +
+ ))} +
+
+
+
+ ); +}; + +export default function JsonComparisonTableContainer({ + data, + isKeysVisible, + screenOffsetTop, + keysStyles, + difference, + isKeysScrollable, + isAlternateColStyle, + isAlternateRowStyle, +}) { + return ( +
+ +
+ ); +} diff --git a/components/Loaders/LinearLoader/index.js b/components/Loaders/LinearLoader/index.js new file mode 100644 index 0000000..26bb3e4 --- /dev/null +++ b/components/Loaders/LinearLoader/index.js @@ -0,0 +1,19 @@ +import React from 'react'; + +export default function CustomLinearProgress({ isLoading, classNames = {} }) { + return ( +
+ +
+ ); +} diff --git a/components/Table/table.js b/components/Table/table.js new file mode 100644 index 0000000..a1ffe5a --- /dev/null +++ b/components/Table/table.js @@ -0,0 +1,28 @@ +import React from 'react'; +import TableHeader from './tableHeader'; +import TableBody from './tableBody'; + +const Table = ({ headers, data, onEdit, onDelete, action }) => { + return ( +
+ + + {headers.map((header) => ( + + ))} + + + + +
+
+ ); +}; + +export default Table; diff --git a/components/Table/tableBody.js b/components/Table/tableBody.js new file mode 100644 index 0000000..1fa356d --- /dev/null +++ b/components/Table/tableBody.js @@ -0,0 +1,39 @@ +import React from 'react'; + +const TableBody = ({ data, headers, onEdit, onDelete, action = true }) => { + return ( + + {data?.map((row, rowIndex) => ( + + {headers.map((header) => ( + + {/* Allow React components or plain text */} + {typeof row[header.key] === 'function' ? row[header.key]() : row[header.key]} + + ))} + {action && ( + +
+ {onEdit && ( + + )} + +
+ + )} + + ))} + + ); +}; + +export default TableBody; diff --git a/components/Table/tableHeader.js b/components/Table/tableHeader.js new file mode 100644 index 0000000..ffa02df --- /dev/null +++ b/components/Table/tableHeader.js @@ -0,0 +1,26 @@ +import React from 'react'; + +const TableHeader = ({ headers, action = true }) => { + return ( + + + {headers.map((header) => ( + + {header.label} + + ))} + {action && ( + Actions + )} + + + ); +}; + +export default TableHeader; diff --git a/hooks/Header/addHeader.js b/hooks/Header/addHeader.js new file mode 100644 index 0000000..9b9f0c4 --- /dev/null +++ b/hooks/Header/addHeader.js @@ -0,0 +1,37 @@ +import { useContext, useEffect } from 'react'; +import { getApplication } from '../../Queries/Application'; + +const { AppContext } = require('../../libs/context'); +const { getCloudAccounts } = require('../../Queries/CloudAccount'); + +export function useInitializeHeader() { + const { setAppData } = useContext(AppContext); + + const handleAppData = (entity, values) => + setAppData((prevValues) => ({ ...prevValues, [entity]: values })); + + const fetchCloudAccounts = async () => { + try { + const data = await getCloudAccounts(); + handleAppData('CLOUD_ACCOUNT_DATA', { data: data.data, isSuccess: true }); + } catch (error) { + handleAppData('CLOUD_ACCOUNT_DATA', { data: [], isSuccess: false }); + } + }; + + const fetchApplications = async () => { + try { + const data = await getApplication(); + handleAppData('APPLICATION_DATA', { data: data.data, isSuccess: true }); + } catch (error) { + handleAppData('APPLICATION_DATA', { data: [], isSuccess: false }); + } + }; + + useEffect(() => { + fetchCloudAccounts(); + fetchApplications(); + }, []); + + return {}; +} diff --git a/hooks/application/getApplicationList.js b/hooks/application/getApplicationList.js index b8062cb..3d0404c 100644 --- a/hooks/application/getApplicationList.js +++ b/hooks/application/getApplicationList.js @@ -7,7 +7,7 @@ const useApplicationList = () => { const [error, setError] = useState(null); useEffect(() => { - const fetchCloudAccounts = async () => { + const fetchApplications = async () => { try { const data = await getApplication(); setApplications(data); @@ -18,7 +18,7 @@ const useApplicationList = () => { } }; - fetchCloudAccounts(); + fetchApplications(); }, []); return { applications, loading, error }; diff --git a/hooks/deploymentSpace/addDeploymentSpace.js b/hooks/deploymentSpace/addDeploymentSpace.js new file mode 100644 index 0000000..b658fc3 --- /dev/null +++ b/hooks/deploymentSpace/addDeploymentSpace.js @@ -0,0 +1,37 @@ +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { addDeploymentConfig } from '../../Queries/DeploymentSpace'; + +const useAddDeploymentConfig = () => { + const router = useRouter(); + const params = useParams(); + // const [values, setValues] = useState({ name: '' }); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + const handleSubmit = async (id, values) => { + setIsLoading(true); + setError(null); + + try { + const data = await addDeploymentConfig(id, values); + setError(null); + router.push(`/applications/${params?.['application-id']}/deploymentSpace`); + return data; + } catch (error) { + setError(error.message); + } finally { + setIsLoading(false); + } + }; + + return { + // values, + // setValues, + handleSubmit, + isLoading, + error, + }; +}; + +export default useAddDeploymentConfig; diff --git a/hooks/deploymentSpace/getDeploymentSpace.js b/hooks/deploymentSpace/getDeploymentSpace.js new file mode 100644 index 0000000..4581ed4 --- /dev/null +++ b/hooks/deploymentSpace/getDeploymentSpace.js @@ -0,0 +1,108 @@ +import { useEffect, useState } from 'react'; +import { getApplicationById } from '../../Queries/Application'; +import { useParams } from 'next/navigation'; + +const datas = { + data: { + id: 1, + name: 'application_1', + environments: [ + { + id: 1, + name: 'prod', + level: 1, + applicationID: 1, + deploymentSpace: null, + createdAt: '2024-12-15T18:11:52Z', + updatedAt: '2024-12-15T18:11:52Z', + }, + { + id: 2, + name: 'test', + level: 3, + applicationID: 1, + deploymentSpace: null, + createdAt: '2024-12-15T18:11:52Z', + updatedAt: '2024-12-15T18:11:52Z', + }, + { + id: 3, + name: 'stage', + level: 2, + applicationID: 1, + deploymentSpace: null, + createdAt: '2024-12-15T18:11:52Z', + updatedAt: '2024-12-15T18:11:52Z', + }, + { + id: 4, + name: 'env3', + level: 0, + applicationID: 1, + deploymentSpace: null, + createdAt: '2024-12-15T18:12:07Z', + updatedAt: '2024-12-15T18:12:07Z', + }, + { + id: 5, + name: 'Test-env', + level: 4, + applicationID: 1, + deploymentSpace: null, + createdAt: '2024-12-16T06:29:49Z', + updatedAt: '2024-12-16T06:29:49Z', + }, + { + id: 6, + name: 'stage', + level: 1, + applicationId: 1, + deploymentSpace: { + name: 'exampleName', + next: { + name: 'gke', + next: { + name: 'test-gcp-04dec', + next: { + name: 'monitoring', + next: null, + }, + }, + }, + }, + createdAt: '2024-12-15T16:05:06Z', + updatedAt: '2024-12-15T16:05:06Z', + }, + ], + createdAt: '2024-12-15T18:11:52Z', + updatedAt: '2024-12-15T18:11:52Z', + }, +}; + +const useGetDeploymentSpace = () => { + const params = useParams(); + + const [value, setValue] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const getAppById = async () => { + try { + const data = await getApplicationById(params?.['application-id']); + setValue(data); + return value; + } catch (e) { + setError(e); + } finally { + setLoading(false); + } + }; + + getAppById(); + }, []); + + return { value, loading, error }; +}; + +export default useGetDeploymentSpace; diff --git a/hooks/environment/addEnvironment.js b/hooks/environment/addEnvironment.js new file mode 100644 index 0000000..eec065e --- /dev/null +++ b/hooks/environment/addEnvironment.js @@ -0,0 +1,37 @@ +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { addEnvironment } from '../../Queries/Application'; + +const useAddEnvironment = () => { + const router = useRouter(); + const params = useParams(); + const [values, setValues] = useState({ name: '' }); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + const handleSubmit = async (id, values) => { + setIsLoading(true); + setError(null); + + try { + const data = await addEnvironment(id, values); + setError(null); + router.push(`/applications/${params?.['application-id']}/deploymentSpace`); + return data; + } catch (error) { + setError(error.message); + } finally { + setIsLoading(false); + } + }; + + return { + values, + setValues, + handleSubmit, + isLoading, + error, + }; +}; + +export default useAddEnvironment; diff --git a/libs/context.js b/libs/context.js new file mode 100644 index 0000000..2459265 --- /dev/null +++ b/libs/context.js @@ -0,0 +1,18 @@ +'use client'; + +import { createContext, useState } from 'react'; + +function ContextProvider({ children }) { + const [appData, setAppData] = useState({ + CLOUD_ACCOUNT_DATA: { data: [], isSuccess: false }, + // CLUSTERS_DATA: { data: [], isSuccess: false, isError: false }, + // NAMESPACE_DATA: { data: [], isSuccess: false }, + APPLICATION_DATA: { data: [], isSuccess: false }, + }); + + return {children}; +} + +export default ContextProvider; + +export const AppContext = createContext(); diff --git a/partials/Environment/addForm.js b/partials/Environment/addForm.js new file mode 100644 index 0000000..340e907 --- /dev/null +++ b/partials/Environment/addForm.js @@ -0,0 +1,65 @@ +import React from 'react'; +import Label from '../../components/Label'; +import Input from '../../components/Input'; +import useAddEnvironment from '../../hooks/environment/addEnvironment'; +import Button from '../../components/Button'; +import ErrorComponent from '../../components/ErrorComponent'; +import { useParams, usePathname, useRouter } from 'next/navigation'; + +const AddEnvironment = () => { + const router = useParams(); + const { values, setValues, handleSubmit, isLoading, error } = useAddEnvironment(); + + const handleChange = (e) => { + setValues({ ...values, [e.target.name]: e.target.value }); + }; + + const handleOnSubmit = (e) => { + e.preventDefault(); + handleSubmit(router?.['application-id'], values); + }; + + return ( +
+
+
+ +
+ } + // inputProps={provider === 'azure' ? { minLength: 6 } : { maxLength: 16, minLength: 6 }} + /> +
+
+ +
+ {error && ( + + )} +
+
+
+ +
+
+ ); +}; + +export default AddEnvironment; diff --git a/partials/Header/index.js b/partials/Header/index.js index a132c4a..cc67dcd 100644 --- a/partials/Header/index.js +++ b/partials/Header/index.js @@ -1,6 +1,6 @@ 'use client'; -import { Fragment, useEffect, useState } from 'react'; +import { Fragment, useContext, useEffect, useState } from 'react'; import { Disclosure, Transition, Dialog } from '@headlessui/react'; import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'; import AppLogo from '../../svg/appLogo'; @@ -10,6 +10,8 @@ import { usePathname, useRouter } from 'next/navigation'; import CloudAccountSVG from '../../svg/cloudAccount'; import APP_LOGO_IMAGE from '../../public/applogoWithText.svg'; import ApplicationSvg from '../../svg/application'; +import { useInitializeHeader } from '../../hooks/Header/addHeader'; +import { AppContext } from '../../libs/context'; function classNames(...classes) { return classes.filter(Boolean).join(' '); @@ -18,8 +20,10 @@ function classNames(...classes) { export function TopBar() { const router = useRouter(); const pathname = usePathname(); + useInitializeHeader(); const [tab, setTab] = useState(); const [sidebarOpen, setSidebarOpen] = useState(false); + const { appData } = useContext(AppContext); useEffect(() => { let DashboardView = navigation diff --git a/partials/cloudAccount/createForm.js b/partials/cloudAccount/createForm.js index 910ddfb..278852b 100644 --- a/partials/cloudAccount/createForm.js +++ b/partials/cloudAccount/createForm.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import Button from '../../components/Button'; import Label from '../../components/Label'; import Input from '../../components/Input'; diff --git a/partials/draggableList/index.js b/partials/draggableList/index.js index f478ec7..ea839d6 100644 --- a/partials/draggableList/index.js +++ b/partials/draggableList/index.js @@ -1,7 +1,7 @@ import { ChevronRightIcon } from '@heroicons/react/20/solid'; import { useEffect, useRef, useState } from 'react'; -export default function DraggableList({ chips, handleListUpdate }) { +export default function DraggableList({ chips, handleListUpdate = () => {}, disableDrag = false }) { const [pages, setPages] = useState([]); useEffect(() => { @@ -16,14 +16,12 @@ export default function DraggableList({ chips, handleListUpdate }) { const dragOverItem = useRef(); const handleDragStart = (e, position) => { + if (disableDrag) return; draggingItem.current = position; }; - // const handleDragEnter = (e, position) => { - // dragOverItem.current = position; - // }; - const handleDragEnter = (e, position) => { + if (disableDrag) return; dragOverItem.current = position; const listCopy = [...pages]; const draggingItemContent = listCopy[draggingItem.current]; @@ -42,17 +40,6 @@ export default function DraggableList({ chips, handleListUpdate }) { handleListUpdate(updatedList); }; - // const handleDragEnd = () => { - // const listCopy = [...pages]; - // const draggingItemContent = listCopy[draggingItem.current]; - // listCopy.splice(draggingItem.current, 1); - // listCopy.splice(dragOverItem.current, 0, draggingItemContent); - - // draggingItem.current = null; - // dragOverItem.current = null; - // setPages(listCopy); - // }; - return (