diff --git a/Queries/CronJob/index.js b/Queries/CronJob/index.js new file mode 100644 index 0000000..b6ae41d --- /dev/null +++ b/Queries/CronJob/index.js @@ -0,0 +1,24 @@ +import apiClient from '../../utlis/apiClient'; +import { ENVIRONMENT_ENDPOINT } from '../../utlis/apiEndpoints'; + +const url = ENVIRONMENT_ENDPOINT; + +export const getCronJobs = async (id) => { + const updatedUrl = `${url}/${id}/deploymentspace/cronjob`; + try { + const data = await apiClient.get(updatedUrl); + return data; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to fetch cron jobs'); + } +}; + +export const getCronByName = async (id, name) => { + const updatedUrl = `${url}/${id}/deploymentspace/cronjob/${name}`; + try { + const data = await apiClient.get(updatedUrl); + return data; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to fetch cron job details'); + } +}; diff --git a/Queries/Deployment/index.js b/Queries/Deployment/index.js new file mode 100644 index 0000000..9fbcd12 --- /dev/null +++ b/Queries/Deployment/index.js @@ -0,0 +1,24 @@ +import apiClient from '../../utlis/apiClient'; +import { ENVIRONMENT_ENDPOINT } from '../../utlis/apiEndpoints'; + +const url = ENVIRONMENT_ENDPOINT; + +export const getDeployment = async (id) => { + const updatedUrl = `${url}/${id}/deploymentspace/deployment`; + try { + const data = await apiClient.get(updatedUrl); + return data; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to fetch deployment'); + } +}; + +export const getDeploymentByName = async (id, name) => { + const updatedUrl = `${url}/${id}/deploymentspace/deployment/${name}`; + try { + const data = await apiClient.get(updatedUrl); + return data; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to fetch deployment details'); + } +}; diff --git a/Queries/Pods/index.js b/Queries/Pods/index.js new file mode 100644 index 0000000..c3f8340 --- /dev/null +++ b/Queries/Pods/index.js @@ -0,0 +1,24 @@ +import apiClient from '../../utlis/apiClient'; +import { ENVIRONMENT_ENDPOINT } from '../../utlis/apiEndpoints'; + +const url = ENVIRONMENT_ENDPOINT; + +export const getPods = async (id) => { + const updatedUrl = `${url}/${id}/deploymentspace/pod`; + try { + const data = await apiClient.get(updatedUrl); + return data; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to fetch pods'); + } +}; + +export const getPodByName = async (id, name) => { + const updatedUrl = `${url}/${id}/deploymentspace/pod/${name}`; + try { + const data = await apiClient.get(updatedUrl); + return data; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to fetch pod details'); + } +}; diff --git a/Queries/Service/index.js b/Queries/Service/index.js new file mode 100644 index 0000000..e4b2f4b --- /dev/null +++ b/Queries/Service/index.js @@ -0,0 +1,24 @@ +import apiClient from '../../utlis/apiClient'; +import { ENVIRONMENT_ENDPOINT } from '../../utlis/apiEndpoints'; + +const url = ENVIRONMENT_ENDPOINT; + +export const getService = async (id) => { + const updatedUrl = `${url}/${id}/deploymentspace/service`; + try { + const data = await apiClient.get(updatedUrl); + return data; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to fetch services'); + } +}; + +export const getServiceByName = async (id, name) => { + const updatedUrl = `${url}/${id}/deploymentspace/service/${name}`; + try { + const data = await apiClient.get(updatedUrl); + return data; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to fetch service details'); + } +}; diff --git a/README.md b/README.md index 1e99ce3..d34e2a3 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Zop is a comprehensive tool for managing cloud infrastructure. It consists of th Run the following command to pull and start the Docker image for the zop-api: ```bash - docker run -d -p 8000:8000 --name zop-api zopdev/zop-api:v0.0.2 + docker run -d -p 8000:8000 --name zop-api zopdev/zop-api:v0.0.3 ``` #### zop-ui @@ -32,7 +32,7 @@ Run the following command to pull and start the Docker image for the zop-api: Run the following command to pull and start the Docker image for the zop-ui: ```bash - docker run -d -p 3000:3000 -e NEXT_PUBLIC_API_BASE_URL='http://localhost:8000' --name zop-ui zopdev/zop-ui:v0.0.2 + docker run -d -p 3000:3000 -e NEXT_PUBLIC_API_BASE_URL='http://localhost:8000' --name zop-ui zopdev/zop-ui:v0.0.3 ``` > **Note:** The environment variable `NEXT_PUBLIC_API_BASE_URL` is used by zop-ui to connect to the diff --git a/components/Button/IconButton/index.js b/components/Button/IconButton/index.js new file mode 100644 index 0000000..af3e4c2 --- /dev/null +++ b/components/Button/IconButton/index.js @@ -0,0 +1,19 @@ +import React from 'react'; + +const IconButton = ({ disabled, onClick, children, style, ...rest }) => { + return ( + + ); +}; + +export default IconButton; diff --git a/components/Cards/applicationCard.js b/components/Cards/applicationCard.js index 4665ab5..0740dfc 100644 --- a/components/Cards/applicationCard.js +++ b/components/Cards/applicationCard.js @@ -12,7 +12,7 @@ const ApplicationCard = ({ data, view }) => { }, [data?.id, router]); const handleRedirect = () => { - router.push(`/applications/${data?.id}/deploymentSpace`); + router.push(`/applications/${data?.id}/environment`); }; return ( diff --git a/components/FullScreenDrawer/index.js b/components/FullScreenDrawer/index.js new file mode 100644 index 0000000..4fa630c --- /dev/null +++ b/components/FullScreenDrawer/index.js @@ -0,0 +1,104 @@ +'use client'; + +import React, { useState, Fragment } from 'react'; +import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react'; +import { XMarkIcon } from '@heroicons/react/24/outline'; +import IconButton from '../Button/IconButton'; + +export function FullScreenDrawer({ + title, + RenderIcon, + RenderComponent, + disabled, + isIcon, + handleMenuItemClose = () => {}, + formData, +}) { + const [open, setOpen] = useState(false); + + const handleClickOpen = (e) => { + e.stopPropagation(); + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + + const handleSuccess = () => { + setOpen(false); + }; + + return ( + <> + {isIcon && ( + // + handleClickOpen(e)} disabled={disabled}> + {RenderIcon} + + // + )} + + + + +
+ + +
+
+
+ + +
+
+
+ + {title} + +
+ { + handleClose(); + handleMenuItemClose(); + }} + > + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + ); +} diff --git a/components/Table/table.js b/components/Table/table.js index a1ffe5a..d74f134 100644 --- a/components/Table/table.js +++ b/components/Table/table.js @@ -2,15 +2,24 @@ import React from 'react'; import TableHeader from './tableHeader'; import TableBody from './tableBody'; -const Table = ({ headers, data, onEdit, onDelete, action }) => { +const Table = ({ + headers, + data, + onEdit, + onDelete, + action, + handleRowClick = () => {}, + renderComponent, + enableRowClick, +}) => { return ( -
- +
+
{headers.map((header) => ( ))} - + { onEdit={onEdit} onDelete={onDelete} action={action} + handleRowClick={handleRowClick} + renderComponent={renderComponent} + enableRowClick={enableRowClick} />
diff --git a/components/Table/tableBody.js b/components/Table/tableBody.js index 1fa356d..1a6e12a 100644 --- a/components/Table/tableBody.js +++ b/components/Table/tableBody.js @@ -1,16 +1,32 @@ import React from 'react'; +import { FullScreenDrawer } from '../FullScreenDrawer'; +import { DocumentChartBarIcon } from '@heroicons/react/24/outline'; -const TableBody = ({ data, headers, onEdit, onDelete, action = true }) => { +const TableBody = ({ + data, + headers, + onEdit, + onDelete, + action = true, + handleRowClick = () => {}, + renderComponent, + enableRowClick = false, +}) => { return ( {data?.map((row, rowIndex) => ( - + enableRowClick && handleRowClick(row)} + > {headers.map((header) => ( {/* Allow React components or plain text */} {typeof row[header.key] === 'function' ? row[header.key]() : row[header.key]} @@ -18,15 +34,32 @@ const TableBody = ({ data, headers, onEdit, onDelete, action = true }) => { ))} {action && ( -
+
{onEdit && ( )} - + {onDelete && ( + + )} + {renderComponent && ( +
)} diff --git a/components/Table/tableHeader.js b/components/Table/tableHeader.js index ffa02df..e71966d 100644 --- a/components/Table/tableHeader.js +++ b/components/Table/tableHeader.js @@ -8,7 +8,7 @@ const TableHeader = ({ headers, action = true }) => { @@ -16,7 +16,7 @@ const TableHeader = ({ headers, action = true }) => { ))} {action && ( - Actions + Actions )} diff --git a/hooks/cronJob/getCronJobDetails.js b/hooks/cronJob/getCronJobDetails.js new file mode 100644 index 0000000..7ccdc7c --- /dev/null +++ b/hooks/cronJob/getCronJobDetails.js @@ -0,0 +1,29 @@ +import { useState, useEffect } from 'react'; +import { useParams } from 'next/navigation'; +import { getCronByName } from '../../Queries/CronJob'; + +const useCronJobDetails = (formData) => { + const params = useParams(); + const [cronJob, setCronJob] = useState(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchCronJobDetails = async () => { + try { + const data = await getCronByName(params?.['environment-id'], formData?.name); + setCronJob(data); + } catch (error) { + setError(error.message); + } finally { + setLoading(false); + } + }; + + fetchCronJobDetails(); + }, []); + + return { cronJob, loading, error }; +}; + +export default useCronJobDetails; diff --git a/hooks/cronJob/getCronJobList.js b/hooks/cronJob/getCronJobList.js new file mode 100644 index 0000000..2e31eda --- /dev/null +++ b/hooks/cronJob/getCronJobList.js @@ -0,0 +1,29 @@ +import { useState, useEffect } from 'react'; +import { getCronJobs } from '../../Queries/CronJob'; +import { useParams } from 'next/navigation'; + +const useCronJobList = () => { + const params = useParams(); + const [cronJob, setCronJob] = useState(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchCronJob = async () => { + try { + const data = await getCronJobs(params?.['environment-id']); + setCronJob(data); + } catch (error) { + setError(error.message); + } finally { + setLoading(false); + } + }; + + fetchCronJob(); + }, []); + + return { cronJob, loading, error }; +}; + +export default useCronJobList; diff --git a/hooks/deployment/getDeployment.js b/hooks/deployment/getDeployment.js new file mode 100644 index 0000000..a6652b6 --- /dev/null +++ b/hooks/deployment/getDeployment.js @@ -0,0 +1,30 @@ +import { useState, useEffect } from 'react'; +import { getService } from '../../Queries/Service'; +import { useParams } from 'next/navigation'; +import { getDeployment } from '../../Queries/Deployment'; + +const useDeploymentList = () => { + const params = useParams(); + const [deployment, setDeployment] = useState(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchDeployment = async () => { + try { + const data = await getDeployment(params?.['environment-id']); + setDeployment(data); + } catch (error) { + setError(error.message); + } finally { + setLoading(false); + } + }; + + fetchDeployment(); + }, []); + + return { deployment, loading, error }; +}; + +export default useDeploymentList; diff --git a/hooks/deployment/getDeploymentDetails.js b/hooks/deployment/getDeploymentDetails.js new file mode 100644 index 0000000..267ed2d --- /dev/null +++ b/hooks/deployment/getDeploymentDetails.js @@ -0,0 +1,29 @@ +import { useState, useEffect } from 'react'; +import { useParams } from 'next/navigation'; +import { getDeploymentByName } from '../../Queries/Deployment'; + +const useDeploymentDetails = (formData) => { + const params = useParams(); + const [deployment, setDeployment] = useState(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchDeploymentDetails = async () => { + try { + const data = await getDeploymentByName(params?.['environment-id'], formData?.name); + setDeployment(data); + } catch (error) { + setError(error.message); + } finally { + setLoading(false); + } + }; + + fetchDeploymentDetails(); + }, []); + + return { deployment, loading, error }; +}; + +export default useDeploymentDetails; diff --git a/hooks/deploymentSpace/addDeploymentSpace.js b/hooks/deploymentSpace/addDeploymentSpace.js index b658fc3..c4ca4a6 100644 --- a/hooks/deploymentSpace/addDeploymentSpace.js +++ b/hooks/deploymentSpace/addDeploymentSpace.js @@ -5,7 +5,6 @@ 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); @@ -16,7 +15,7 @@ const useAddDeploymentConfig = () => { try { const data = await addDeploymentConfig(id, values); setError(null); - router.push(`/applications/${params?.['application-id']}/deploymentSpace`); + router.push(`/applications/${params?.['application-id']}/environment`); return data; } catch (error) { setError(error.message); @@ -26,8 +25,6 @@ const useAddDeploymentConfig = () => { }; return { - // values, - // setValues, handleSubmit, isLoading, error, diff --git a/hooks/deploymentSpace/getDeploymentSpace.js b/hooks/deploymentSpace/getDeploymentSpace.js index 4581ed4..9429f56 100644 --- a/hooks/deploymentSpace/getDeploymentSpace.js +++ b/hooks/deploymentSpace/getDeploymentSpace.js @@ -2,83 +2,6 @@ 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(); diff --git a/hooks/environment/addEnvironment.js b/hooks/environment/addEnvironment.js index eec065e..911ce5e 100644 --- a/hooks/environment/addEnvironment.js +++ b/hooks/environment/addEnvironment.js @@ -16,7 +16,7 @@ const useAddEnvironment = () => { try { const data = await addEnvironment(id, values); setError(null); - router.push(`/applications/${params?.['application-id']}/deploymentSpace`); + router.push(`/applications/${params?.['application-id']}/environment`); return data; } catch (error) { setError(error.message); diff --git a/hooks/pods/getPodDetails.js b/hooks/pods/getPodDetails.js new file mode 100644 index 0000000..9d7b770 --- /dev/null +++ b/hooks/pods/getPodDetails.js @@ -0,0 +1,29 @@ +import { useState, useEffect } from 'react'; +import { useParams } from 'next/navigation'; +import { getPodByName } from '../../Queries/Pods'; + +const usePodDetails = (formData) => { + const params = useParams(); + const [pod, setPod] = useState(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchPodDetails = async () => { + try { + const data = await getPodByName(params?.['environment-id'], formData?.name); + setPod(data); + } catch (error) { + setError(error.message); + } finally { + setLoading(false); + } + }; + + fetchPodDetails(); + }, []); + + return { pod, loading, error }; +}; + +export default usePodDetails; diff --git a/hooks/pods/getPodsList.js b/hooks/pods/getPodsList.js new file mode 100644 index 0000000..5a08069 --- /dev/null +++ b/hooks/pods/getPodsList.js @@ -0,0 +1,29 @@ +import { useState, useEffect } from 'react'; +import { useParams } from 'next/navigation'; +import { getPods } from '../../Queries/Pods'; + +const usePodList = () => { + const params = useParams(); + const [pods, setPods] = useState(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchPods = async () => { + try { + const data = await getPods(params?.['environment-id']); + setPods(data); + } catch (error) { + setError(error.message); + } finally { + setLoading(false); + } + }; + + fetchPods(); + }, []); + + return { pods, loading, error }; +}; + +export default usePodList; diff --git a/hooks/service/getServiceDetails.js b/hooks/service/getServiceDetails.js new file mode 100644 index 0000000..8c1ec75 --- /dev/null +++ b/hooks/service/getServiceDetails.js @@ -0,0 +1,29 @@ +import { useState, useEffect } from 'react'; +import { getServiceByName } from '../../Queries/Service'; +import { useParams } from 'next/navigation'; + +const useServiceDetails = (formData) => { + const params = useParams(); + const [service, setService] = useState(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchServiceDetails = async () => { + try { + const data = await getServiceByName(params?.['environment-id'], formData?.name); + setService(data); + } catch (error) { + setError(error.message); + } finally { + setLoading(false); + } + }; + + fetchServiceDetails(); + }, []); + + return { service, loading, error }; +}; + +export default useServiceDetails; diff --git a/hooks/service/getServiceList.js b/hooks/service/getServiceList.js new file mode 100644 index 0000000..cb0f286 --- /dev/null +++ b/hooks/service/getServiceList.js @@ -0,0 +1,29 @@ +import { useState, useEffect } from 'react'; +import { getService } from '../../Queries/Service'; +import { useParams } from 'next/navigation'; + +const useServiceList = () => { + const params = useParams(); + const [services, setServices] = useState(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchServices = async () => { + try { + const data = await getService(params?.['environment-id']); + setServices(data); + } catch (error) { + setError(error.message); + } finally { + setLoading(false); + } + }; + + fetchServices(); + }, []); + + return { services, loading, error }; +}; + +export default useServiceList; diff --git a/partials/cronJob/cronJobDetails.js b/partials/cronJob/cronJobDetails.js new file mode 100644 index 0000000..f277f71 --- /dev/null +++ b/partials/cronJob/cronJobDetails.js @@ -0,0 +1,116 @@ +import React from 'react'; +import { calculateAge } from '../../utlis/helperFunc'; +import useCronJobDetails from '../../hooks/cronJob/getCronJobDetails'; +import ErrorComponent from '../../components/ErrorComponent'; +import CustomLinearProgress from '../../components/Loaders/LinearLoader'; + +const CronJobDetails = ({ formData }) => { + const { cronJob, loading, error } = useCronJobDetails(formData); + + return ( + <> + + {!loading && !error && ( +
+
+

Properties

+

+ This section provides key CronJob details, including creation time, namespace, name, + schedule, labels, annotations, active instances, suspension status, and last scheduled + time. +

+
+
+
+
+
Created
+
+ {calculateAge(cronJob?.data?.metadata?.creationTimestamp)} ago +
+
+
+
Name
+
+ {cronJob?.data?.metadata?.name} +
+
+
+
Namespace
+
+ {cronJob?.data?.metadata?.namespace} +
+
+
+
Labels
+
+
+ {cronJob?.data?.metadata?.labels && + Object?.entries(cronJob?.data?.metadata?.labels).map(([key, value]) => ( + + {`${key}=${value}`} + + ))} +
+
+
+
+
Annotations
+
+
+ {cronJob?.data?.metadata?.annotations && + Object?.entries(cronJob?.data?.metadata?.annotations).map(([key, value]) => ( + + {`${key}=${value}`} + + ))} +
+
+
+
+
Schedule
+
+
{cronJob?.data?.spec?.schedule}
+
+
+
+
Active
+
+
+ {cronJob?.data?.status?.active?.length} +
+
+
+
+
Suspend
+
+
+ {cronJob?.data?.spec?.suspend.toString()} +
+
+
+
+
Last schedule
+
+
+ {calculateAge(cronJob?.data?.status?.lastScheduleTime)} +
+
+
+
+
+
+ )} + {error && ( + + )} + + ); +}; + +export default CronJobDetails; diff --git a/partials/deployments/deploymentDetails.js b/partials/deployments/deploymentDetails.js new file mode 100644 index 0000000..5f40f91 --- /dev/null +++ b/partials/deployments/deploymentDetails.js @@ -0,0 +1,150 @@ +import React from 'react'; +import useDeploymentDetails from '../../hooks/deployment/getDeploymentDetails'; +import { calculateAge } from '../../utlis/helperFunc'; +import CustomLinearProgress from '../../components/Loaders/LinearLoader'; +import ErrorComponent from '../../components/ErrorComponent'; + +const StatusColorCode = { + AVAILABLE: 'text-green-700 bg-green-50 ring-green-300', + PROGRESSING: 'text-primary-700 bg-primary-50 ring-primary-300', +}; + +const DeploymentDetails = ({ formData }) => { + const { deployment, loading, error } = useDeploymentDetails(formData); + return ( + <> + + {!loading && !error && ( +
+ <> +
+

Properties

+

+ The Properties section summarizes the deployment's configuration and state, + including creation time, name, namespace, labels, annotations, replicas, selectors, + strategy type, and status conditions. +

+
+
+
+
+
Created
+
+ {calculateAge(deployment?.data?.metadata?.creationTimestamp)} ago +
+
+
+
Name
+
+ {deployment?.data?.metadata?.name} +
+
+
+
Namespace
+
+ {deployment?.data?.metadata?.namespace} +
+
+
+
Labels
+
+
+ {deployment?.data?.metadata?.labels && + Object?.entries(deployment?.data?.metadata?.labels).map(([key, value]) => ( + + {`${key}=${value}`} + + ))} +
+
+
+
+
Annotations
+
+
+ {deployment?.data?.metadata?.annotations && + Object?.entries(deployment?.data?.metadata?.annotations).map( + ([key, value]) => ( + + {`${key}=${value}`} + + ), + )} +
+
+
+
+
Replicas
+
+
+ {deployment?.data?.status?.replicas} desired,{' '} + {deployment?.data?.status?.availableReplicas} available,{' '} + {deployment?.data?.status?.replicas - + deployment?.data?.status?.availableReplicas}{' '} + unavailable +
+
+
+
+
Selectors
+
+
+ {deployment?.data?.spec?.selector?.matchLabels && + Object?.entries(deployment?.data?.spec?.selector?.matchLabels).map( + ([key, value]) => ( + + {`${key}=${value}`} + + ), + )} +
+
+
+
+
Strategy Type
+
+ {deployment?.data?.spec?.strategy?.type} +
+
+
+
Conditions
+
+
+ {deployment?.data?.status?.conditions && + Object?.entries(deployment?.data?.status?.conditions).map( + ([key, value]) => ( + + {`${value?.type}`} + + ), + )} +
+
+
+
+
+ +
+ )} + {error && ( + + )} + + ); +}; + +export default DeploymentDetails; diff --git a/partials/pods/podsDetails.js b/partials/pods/podsDetails.js new file mode 100644 index 0000000..314f995 --- /dev/null +++ b/partials/pods/podsDetails.js @@ -0,0 +1,313 @@ +import React from 'react'; +import usePodDetails from '../../hooks/pods/getPodDetails'; +import CustomLinearProgress from '../../components/Loaders/LinearLoader'; +import ErrorComponent from '../../components/ErrorComponent'; + +const PodsDetails = ({ formData }) => { + const { pod, loading, error } = usePodDetails(formData); + return ( + <> + + {!loading && !error && ( +
+ <> +
+

Properties

+

+ The Properties section offers a concise overview of the Pod's metadata, + configuration, and status, including creation details, labels, annotations, and + operational insights for easy management. +

+
+
+
+
+
Created
+
+ {pod?.data?.metadata?.creationTimestamp} +
+
+
+
Name
+
+ {pod?.data?.metadata?.name} +
+
+
+
Namespace
+
+ {pod?.data?.metadata?.namespace} +
+
+
+
Labels
+
+
+ {pod?.data?.metadata?.labels && + Object?.entries(pod?.data?.metadata?.labels).map(([key, value]) => ( + + {`${key}=${value}`} + + ))} +
+
+
+
+
Annotations
+
+
+ {pod?.data?.metadata?.annotations && + Object?.entries(pod?.data?.metadata?.annotations).map(([key, value]) => ( + + {`${key}=${value}`} + + ))} +
+
+
+
+
Controlled By
+
+ {pod?.data?.metadata?.ownerReferences?.[0]?.kind} +
+
+
+
Node
+
+ {pod?.data?.spec?.nodeName} +
+
+
+
Pod IP
+
+ {pod?.data?.status?.podIP} +
+
+
+
Service Account
+
+ {pod?.data?.spec?.serviceAccountName} +
+
+
+
Conditions
+
+
+ {pod?.data?.status?.conditions.map((container, index) => ( +
+ {container.type} +
+ ))} +
+
+
+
+
+ +
+
+

Containers

+

+ This section provides detailed insights into each container's configuration, + including runtime settings, resource allocations, environment variables, ports, and + volume mounts. +

+
+ + {pod?.data?.spec?.containers?.map((container, index) => { + return ( +
+
+
+

{container.name}

+
+
+
+
+
Status
+
+ {pod?.data?.status?.containerStatuses && + pod?.data?.status?.containerStatuses?.map((item, containerIndex) => { + const stateKeys = Object?.keys(item.state).join(', '); + return {stateKeys}; + })} +
+
+
+
Image
+
+ {container?.image} +
+
+
+
ImagePullPolicy
+
+ {container?.imagePullPolicy} +
+
+
+
Ports
+
+
+ {container?.ports?.map((item, index) => ( + + {`${item.name}:${item.containerPort}/${item.protocol}`} + + ))} +
+
+
+
+
Environments
+
+
+
+ {container?.env.map((env, index) => ( +
+ {env.name}: {env.value || 'N/A'} +
+ ))} +
+
+
+
+
+
Mounts
+
+
+
+ {container?.volumeMounts.map((mount, index) => ( +
+ {mount.name}: {mount.mountPath || 'N/A'} +
+ ))} +
+
+
+
+
+
Liveness
+
+
+
+ {container?.livenessProbe?.httpGet?.path && ( +
+ {container?.livenessProbe?.httpGet?.path} +
+ )} +
+ delay={container?.livenessProbe?.initialDelaySeconds} +
+
+ timeout={container?.livenessProbe?.timeoutSeconds} +
+
+ period={container?.livenessProbe?.periodSeconds} +
+
+ #success={container?.livenessProbe?.successThreshold} +
+
+ #failure={container?.livenessProbe?.failureThreshold} +
+
+
+
+
+
+
Readiness
+
+
+
+ {container?.readinessProbe?.httpGet?.path && ( +
+ {container?.readinessProbe?.httpGet?.path} +
+ )} +
+ delay={container?.readinessProbe?.initialDelaySeconds} +
+
+ timeout={container?.readinessProbe?.timeoutSeconds} +
+
+ period={container?.readinessProbe?.periodSeconds} +
+
+ #success={container?.readinessProbe?.successThreshold} +
+
+ #failure={container?.readinessProbe?.failureThreshold} +
+
+
+
+
+
+
Arguments
+
+
+
+ {container?.args && + container?.args.map((arg, index) => ( +
+ {arg} +
+ ))} +
+
+
+
+
+
Requests
+
+ CPU - {container?.resources?.requests?.cpu} , Memory -{' '} + {container?.resources?.requests?.memory} +
+
+
+
Limits
+
+ CPU - {container?.resources?.limits?.cpu} , Memory -{' '} + {container?.resources?.limits?.memory} +
+
+
+
+
+ ); + })} +
+
+ )} + {error && ( + + )} + + ); +}; + +export default PodsDetails; diff --git a/partials/service/serviceDetail.js b/partials/service/serviceDetail.js new file mode 100644 index 0000000..38bdb92 --- /dev/null +++ b/partials/service/serviceDetail.js @@ -0,0 +1,135 @@ +import React from 'react'; +import useServiceDetails from '../../hooks/service/getServiceDetails'; +import CustomLinearProgress from '../../components/Loaders/LinearLoader'; +import ErrorComponent from '../../components/ErrorComponent'; +import { calculateAge } from '../../utlis/helperFunc'; + +const ServiceDetails = ({ formData }) => { + const { service, loading, error } = useServiceDetails(formData); + return ( + <> + + {!loading && !error && ( +
+ <> +
+

Properties

+

+ The Properties section provides key details about the service, including its + creation time, name, namespace, labels, selectors, type, and session affinity. It + offers a clear overview of the service's configuration, role, and behavior, aiding + in monitoring and management. +

+
+
+
+
+
Created
+
+ {calculateAge(service?.data?.metadata?.creationTimestamp)} +
+
+
+
Name
+
+ {service?.data?.metadata?.name} +
+
+
+
Namespace
+
+ {service?.data?.metadata?.namespace} +
+
+
+
Labels
+
+
+ {service?.data?.metadata?.labels && + Object?.entries(service?.data?.metadata?.labels).map(([key, value]) => ( + + {`${key}=${value}`} + + ))} +
+
+
+
+
Selectors
+
+
+ {service?.data?.spec?.selector && + Object?.entries(service?.data?.spec?.selector).map(([key, value]) => ( + + {`${key}=${value}`} + + ))} +
+
+
+
+
Type
+
+ {service?.data?.spec?.type} +
+
+
+
Session Affinity
+
+ {service?.data?.spec?.sessionAffinity} +
+
+
+
+ +
+
+

Connection

+

+ The Connection section details the service's network setup, including its Cluster IP + for internal routing and port mappings with target ports and protocols. It provides + a clear view of the service's accessibility and network configuration. +

+
+
+
+
+
Cluster IP
+
+ {service?.data?.spec?.clusterIP} +
+
+
+
Ports
+
+
+ {service?.data?.spec?.ports?.map((item, index) => ( + + {`${item.port}:${item.targetPort}/${item.protocol}`} + + ))} +
+
+
+
+
+
+
+ )} + {error && ( + + )} + + ); +}; + +export default ServiceDetails; diff --git a/partials/sideBar/index.js b/partials/sideBar/index.js index ca478a1..567c63d 100644 --- a/partials/sideBar/index.js +++ b/partials/sideBar/index.js @@ -1,25 +1,13 @@ import { useEffect, useState, useRef } from 'react'; -// import { -// Alerts, -// AuditLog, -// Databases, -// Infra, -// LogSvg, -// Metrics, -// Permission, -// Services, -// Settings, -// SummaryIcon, -// TracesIcon, -// } from '../svg/sidebar'; import Link from 'next/link'; -import { useParams, usePathname, useRouter, useSearchParams } from 'next/navigation'; +import { useParams, usePathname, useRouter } from 'next/navigation'; import { ConfigDiff } from '../../svg/configDiff'; -import { DeploymentSpace } from '../../svg/deploymentSpace'; -// import CronJob from '../svg/sidebar/cronJob'; -// import HelmPackageIcon from '../svg/sidebar/helm_package'; -// import { Profile } from '../svg/sidebar/profile'; +import { Environment } from '../../svg/environment'; +import { Services } from '../../svg/services'; +import DeploymentIcon from '../../svg/deployment'; +import { Cron } from '../../svg/cron'; +import { Pods } from '../../svg/pods'; function classNames(...classes) { return classes.filter(Boolean).join(' '); @@ -76,9 +64,9 @@ export default function Sidebar({ showMobileBar }) { const appOption = [ { accessTo: 'all', - link: `applications/${searchParams['application-id']}/deploymentSpace`, - text: 'Deployment Space', - icon: DeploymentSpace, + link: `applications/${searchParams['application-id']}/environment`, + text: 'Environment', + icon: Environment, }, { accessTo: 'all', @@ -86,128 +74,39 @@ export default function Sidebar({ showMobileBar }) { text: 'Config Diff', icon: ConfigDiff, }, - // { - // accessTo: 'all', - // link: `applications/${searchParams['application-id']}/cron-jobs`, - // text: 'Cron Jobs', - // // icon: CronJob, - // }, - // { - // accessTo: 'all', - // link: `applications/${searchParams['application-id']}/datastore`, - // text: 'Data Store', - // // icon: Databases, - // }, - // { - // accessTo: 'all', - // link: `applications/${searchParams['application-id']}/helm-package`, - // text: 'Helm Package', - // // icon: HelmPackageIcon, - // }, - // { - // accessTo: 'all', - // heading: true, - // headingTitle: 'Observability', - // }, - // { - // accessTo: 'all', - // link: `applications/${searchParams['application-id']}/logs`, - // text: 'Logs', - // // icon: LogSvg, - // }, - // { - // accessTo: 'all', - // link: `applications/${searchParams['application-id']}/traces`, - // text: 'Traces', - // // icon: TracesIcon, - // }, - // { - // accessTo: 'all', - // link: `applications/${searchParams['application-id']}/metric`, - // text: 'Metrics', - // // icon: Metrics, - // }, - // { - // accessTo: 'all', - // link: `applications/${searchParams['application-id']}/alerts`, - // text: 'Alerts', - // // icon: Alerts, - // }, - // { - // accessTo: 'all', - // heading: true, - // headingTitle: '', - // divider: true, - // }, - // { - // accessTo: 'all', - // link: `applications/${searchParams['application-id']}/audit-log`, - // text: 'Audit Log', - // // icon: AuditLog, - // }, - - // { - // accessTo: 'all', - // link: `applications/${searchParams['application-id']}/permissions`, - // text: 'Permissions', - // // icon: Permission, - // }, - - // { - // accessTo: 'all', - // link: `applications/${searchParams['application-id']}/settings`, - // text: 'Settings', - // // icon: Settings, - // }, ]; - // const observabilityOption = [ - // { - // accessTo: 'all', - // link: `observability`, - // text: 'Observability', - // icon: TracesIcon, - // }, - - // // { - // // accessTo: 'all', - // // link: `observability/integrations`, - // // text: 'Integrations', - // // icon: Settings, - // // }, - // ]; - // const settingsOption = [ - // { - // accessTo: 'all', - // // link: `cloud-accounts/${searchParams['cloud-account-id']}/infrastructure`, - // text: 'Profile', - // icon: Profile, - // current: true, - // }, - // { - // accessTo: 'all', - // // link: `cloud-accounts/${searchParams['cloud-account-id']}/audit-log`, - // text: 'Audit Log', - // icon: AuditLog, - // }, - // { - // accessTo: 'all', - // // link: `cloud-accounts/${searchParams['cloud-account-id']}/permissions`, - // text: 'Permissions', - // icon: Permission, - // }, - - // { - // accessTo: 'all', - // // link: `cloud-accounts/${searchParams['cloud-account-id']}/settings`, - // text: 'Settings', - // icon: Settings, - // }, - // ]; + const serviceOption = [ + { + accessTo: 'all', + link: `applications/${searchParams['application-id']}/environment/${searchParams['environment-id']}/services`, + text: 'Service', + icon: Services, + }, + { + accessTo: 'all', + link: `applications/${searchParams['application-id']}/environment/${searchParams['environment-id']}/cron-job`, + text: 'Cron Job', + icon: Cron, + }, + { + accessTo: 'all', + link: `applications/${searchParams['application-id']}/environment/${searchParams['environment-id']}/deployments`, + text: 'Deployments', + icon: DeploymentIcon, + }, + { + accessTo: 'all', + link: `applications/${searchParams['application-id']}/environment/${searchParams['environment-id']}/pods`, + text: 'Pods', + icon: Pods, + }, + ]; const SideBarView = { // cloudView: cloudOption, appView: appOption, + serviceView: serviceOption, // observability: observabilityOption, // setting: settingsOption, Null: [], @@ -223,14 +122,13 @@ export default function Sidebar({ showMobileBar }) { searchParams['cloud-account-id'] === undefined && searchParams['application-id'] !== undefined ) { - setSelectedView('appView'); + if (searchParams['environment-id'] !== undefined) { + setSelectedView('serviceView'); + } else { + setSelectedView('appView'); + } } - // else if (router.pathname.includes('observability')) { - // setSelectedView('observability'); - // } else if (router.pathname.includes('setting')) { - // setSelectedView('setting'); - // } - }, [router]); + }, [router, searchParams]); useEffect(() => { const list = SideBarView[selectedView].map(function (ele) { @@ -249,87 +147,93 @@ export default function Sidebar({ showMobileBar }) { setValue(DashboardView); }, [pathname, selectedView]); - return showMobileBar ? ( -
- -
- ) : ( -
- +
+ ); } diff --git a/src/app/applications/[application-id]/configDiff/page.js b/src/app/applications/[application-id]/configDiff/page.js index 57eaf6a..c87f950 100644 --- a/src/app/applications/[application-id]/configDiff/page.js +++ b/src/app/applications/[application-id]/configDiff/page.js @@ -55,7 +55,7 @@ const ConfigDiff = () => { { name: 'Applications', link: '/applications' }, { name: appData?.APPLICATION_DATA?.isSuccess ? appName?.name : 'loading...', - link: `/applications/${params?.['application-id']}/deploymentSpace`, + link: `/applications/${params?.['application-id']}/environment`, }, { name: 'Config Diff', diff --git a/src/app/applications/[application-id]/deploymentSpace/[environment-id]/configureDeploymentSpace/page.js b/src/app/applications/[application-id]/environment/[environment-id]/configureDeploymentSpace/page.js similarity index 96% rename from src/app/applications/[application-id]/deploymentSpace/[environment-id]/configureDeploymentSpace/page.js rename to src/app/applications/[application-id]/environment/[environment-id]/configureDeploymentSpace/page.js index 047f452..74d165b 100644 --- a/src/app/applications/[application-id]/deploymentSpace/[environment-id]/configureDeploymentSpace/page.js +++ b/src/app/applications/[application-id]/environment/[environment-id]/configureDeploymentSpace/page.js @@ -20,7 +20,7 @@ const AddDeploymentSpace = () => { { name: 'Applications', link: '/applications' }, { name: appData?.APPLICATION_DATA?.isSuccess ? data?.name : 'loading...', - link: `/applications/${params?.['application-id']}/deploymentSpace`, + link: `/applications/${params?.['application-id']}/environment`, }, { name: 'Configure Deployment Space', diff --git a/src/app/applications/[application-id]/environment/[environment-id]/cron-job/page.js b/src/app/applications/[application-id]/environment/[environment-id]/cron-job/page.js new file mode 100644 index 0000000..ea414b1 --- /dev/null +++ b/src/app/applications/[application-id]/environment/[environment-id]/cron-job/page.js @@ -0,0 +1,98 @@ +'use client'; + +import React, { useContext } from 'react'; +import HeadingComponent from '../../../../../../../components/HeaderComponents'; +import CustomLinearProgress from '../../../../../../../components/Loaders/LinearLoader'; +import Table from '../../../../../../../components/Table/table'; +import ErrorComponent from '../../../../../../../components/ErrorComponent'; +import { calculateAge } from '../../../../../../../utlis/helperFunc'; +import BreadCrumbComp from '../../../../../../../components/BreadCrumb'; +import { useParams } from 'next/navigation'; +import { AppContext } from '../../../../../../../libs/context'; +import useCronJobList from '../../../../../../../hooks/cronJob/getCronJobList'; +import CronJobDetails from '../../../../../../../partials/cronJob/cronJobDetails'; + +const headers = [ + { key: 'name', label: 'Name', align: 'left', colClassName: 'sm:!min-w-[175px]' }, + { key: 'schedule', label: 'Schedule', align: 'left', colClassName: 'sm:!min-w-[175px]' }, + { key: 'suspend', label: 'Suspend', align: 'left', colClassName: 'sm:!min-w-[175px]' }, + { key: 'active', label: 'Active', align: 'left', colClassName: 'sm:!min-w-[175px]' }, + { + key: 'last_schedule', + label: 'Last Schedule', + align: 'left', + colClassName: 'sm:!min-w-[175px]', + }, + { key: 'age', label: 'Age', align: 'left', colClassName: 'sm:!min-w-[175px]' }, +]; + +const CronJobs = () => { + const { cronJob, loading, error } = useCronJobList(); + const { appData } = useContext(AppContext); + const params = useParams(); + + const data = appData?.APPLICATION_DATA?.data?.filter( + (item) => item.id === Number(params?.['application-id']), + )?.[0]; + + const bodyData = cronJob?.data?.cronjobs?.map((item) => { + return { + name: item.metadata.name, + schedule: item.spec.schedule, + suspend: item.spec.suspend.toString(), + active: 0, + last_schedule: calculateAge(item.status.active.lastScheduleTime), + age: calculateAge(item.metadata.creationTimestamp), + }; + }); + + const breadcrumbList = [ + { name: 'Applications', link: '/applications' }, + { + name: appData?.APPLICATION_DATA?.isSuccess ? data?.name : 'loading...', + link: `/applications/${params?.['application-id']}/environment`, + }, + { + name: cronJob?.metadata?.environmentName ? cronJob?.metadata?.environmentName : 'loading...', + link: `/applications/${params?.['application-id']}/environment/${params?.['environment-id']}/services`, + }, + { + name: 'Cron Jobs', + link: `#`, + disable: true, + }, + ]; + + return ( + <> + + + // {