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 (
+
+ {children}
+
+ );
+};
+
+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 && (
onEdit(row)} className="text-blue-500 hover:text-blue-700">
Edit
)}
- onDelete(row)} className="text-red-500 hover:text-red-700">
- Delete
-
+ {onDelete && (
+ onDelete(row)} className="text-red-500 hover:text-red-700">
+ Delete
+
+ )}
+ {renderComponent && (
+
+ }
+ title={row?.name}
+ RenderComponent={renderComponent}
+ formData={row}
+ />
+ )}
)}
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 (
+
+
+
+
+
+
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 ? (
-
-
- {SideBarView[selectedView].map((item, index) => {
- return (
- !item?.heading && (
-
-
-
- {item.text}
-
- )
- );
- })}
-
-
- ) : (
-
-
-
-
-
- {SideBarView[selectedView].map((item, index) => {
- return item?.heading ? (
- item?.divider ? (
-
- ) : (
-
-
- {item?.headingTitle}
+ return (
+ <>
+ {/* Mobile SideBar */}
+
+
+
+ {SideBarView[selectedView].map((item, index) => {
+ return (
+ !item?.heading && (
+
+
+
+ {item.text}
+
+ )
+ );
+ })}
+
+
+
+ {/* fullscreen SideBar */}
+
+
+
+
+
+
+ {SideBarView[selectedView].map((item, index) => {
+ return item?.heading ? (
+ item?.divider ? (
+
-
- )
- ) : (
-
-
-
+
+ {item?.headingTitle}
+
+
+ )
+ ) : (
+
+
-
- {item.text}
-
-
- );
- })}
-
-
-
-
-
+ >
+
+
+ {item.text}
+
+
+ );
+ })}
+
+
+
+
+
+ >
);
}
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 (
+ <>
+
+
+ // { }
+ // Add Cron Job
+ //
+ // }
+ />
+
+
+
+ {error && (
+
+ )}
+ >
+ );
+};
+
+export default CronJobs;
diff --git a/src/app/applications/[application-id]/environment/[environment-id]/deployments/page.js b/src/app/applications/[application-id]/environment/[environment-id]/deployments/page.js
new file mode 100644
index 0000000..e7d06d6
--- /dev/null
+++ b/src/app/applications/[application-id]/environment/[environment-id]/deployments/page.js
@@ -0,0 +1,105 @@
+'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 useDeploymentList from '../../../../../../../hooks/deployment/getDeployment';
+import ErrorComponent from '../../../../../../../components/ErrorComponent';
+import { calculateAge } from '../../../../../../../utlis/helperFunc';
+import DeploymentDetails from '../../../../../../../partials/deployments/deploymentDetails';
+import BreadCrumbComp from '../../../../../../../components/BreadCrumb';
+import { useParams } from 'next/navigation';
+import { AppContext } from '../../../../../../../libs/context';
+
+const headers = [
+ { key: 'name', label: 'Name', align: 'left', colClassName: 'sm:!min-w-[175px]' },
+ { key: 'pods', label: 'Pods', align: 'left', colClassName: 'sm:!min-w-[175px]' },
+ { key: 'replica', label: 'Replicas', align: 'left', colClassName: 'sm:!min-w-[175px]' },
+ { key: 'age', label: 'Age', align: 'left', colClassName: 'sm:!min-w-[175px]' },
+ { key: 'condition', label: 'Conditions', align: 'left', colClassName: 'sm:!min-w-[175px]' },
+];
+
+const Deployments = () => {
+ const { deployment, loading, error } = useDeploymentList();
+ const { appData } = useContext(AppContext);
+ const params = useParams();
+
+ const data = appData?.APPLICATION_DATA?.data?.filter(
+ (item) => item.id === Number(params?.['application-id']),
+ )?.[0];
+
+ const bodyData = deployment?.data?.deployments?.map((item) => {
+ return {
+ name: item.metadata.name,
+ pods: `${item.status.readyReplicas}/${item.spec.replicas}`,
+ replica: item.spec.replicas,
+ age: calculateAge(item.metadata.creationTimestamp),
+ condition: (
+
+ {item.status.conditions
+ .map((condition) => condition.type)
+ .reverse()
+ .join(' ')}
+
+ ),
+ };
+ });
+
+ const breadcrumbList = [
+ { name: 'Applications', link: '/applications' },
+ {
+ name: appData?.APPLICATION_DATA?.isSuccess ? data?.name : 'loading...',
+ link: `/applications/${params?.['application-id']}/environment`,
+ },
+ {
+ name: deployment?.metadata?.environmentName
+ ? deployment?.metadata?.environmentName
+ : 'loading...',
+ link: `/applications/${params?.['application-id']}/environment/${params?.['environment-id']}/services`,
+ },
+ {
+ name: 'Deployments',
+ link: `#`,
+ disable: true,
+ },
+ ];
+
+ return (
+ <>
+
+
+ // { }
+ // Add Deployment
+ //
+ // }
+ />
+
+
+
+ {error && (
+
+ )}
+ >
+ );
+};
+
+export default Deployments;
diff --git a/src/app/applications/[application-id]/environment/[environment-id]/pods/page.js b/src/app/applications/[application-id]/environment/[environment-id]/pods/page.js
new file mode 100644
index 0000000..76386bb
--- /dev/null
+++ b/src/app/applications/[application-id]/environment/[environment-id]/pods/page.js
@@ -0,0 +1,124 @@
+'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 { calculateAge } from '../../../../../../../utlis/helperFunc';
+import ErrorComponent from '../../../../../../../components/ErrorComponent';
+import BreadCrumbComp from '../../../../../../../components/BreadCrumb';
+import { useParams } from 'next/navigation';
+import { AppContext } from '../../../../../../../libs/context';
+import usePodList from '../../../../../../../hooks/pods/getPodsList';
+import PodsDetails from '../../../../../../../partials/pods/podsDetails';
+
+const headers = [
+ { key: 'name', label: 'Name', align: 'left', colClassName: 'sm:!min-w-[300px]' },
+ { key: 'container', label: 'Container', align: 'left', colClassName: 'sm:!min-w-[200px]' },
+ { key: 'restart', label: 'Restart', align: 'left', colClassName: 'sm:!min-w-[200px]' },
+ {
+ key: 'controlled',
+ label: 'Controlled By',
+ align: 'left',
+ colClassName: 'sm:!min-w-[200px] whitespace-nowrap',
+ },
+ // { key: 'Qos', label: 'Qos', align: 'left', colClassName: 'sm:!min-w-[200px]' },
+ { key: 'age', label: 'Age', align: 'left', colClassName: ' sm:!min-w-[200px] whitespace-nowrap' },
+ { key: 'state', label: 'Status', align: 'end', colClassName: 'sm:!min-w-[200px]' },
+];
+
+const StatusBoxes = ({ data }) => {
+ return (
+
+ {data.map((container, index) => (
+
+ ))}
+
+ );
+};
+
+const Services = () => {
+ const { pods, loading, error } = usePodList();
+ const { appData } = useContext(AppContext);
+ const params = useParams();
+
+ const data = appData?.APPLICATION_DATA?.data?.filter(
+ (item) => item.id === Number(params?.['application-id']),
+ )?.[0];
+
+ const bodyData = pods?.data?.pods?.map((item) => {
+ return {
+ name: item.metadata.name,
+ container: ,
+ restart: item?.status?.containerStatuses?.reduce((acc, curr) => acc + curr.restartCount, 0),
+ controlled: item?.metadata?.ownerReferences?.[0]?.kind,
+ age: calculateAge(item.metadata.creationTimestamp),
+ state:
+ item.status.phase === 'Running' || item.status.phase === 'Succeeded' ? (
+
+ {item.status.phase}
+
+ ) : (
+
+ {item.status.phase}
+
+ ),
+ };
+ });
+
+ const breadcrumbList = [
+ { name: 'Applications', link: '/applications' },
+ {
+ name: appData?.APPLICATION_DATA?.isSuccess ? data?.name : 'loading...',
+ link: `/applications/${params?.['application-id']}/environment`,
+ },
+ {
+ name: pods?.metadata?.environmentName ? pods?.metadata?.environmentName : 'loading...',
+ link: `/applications/${params?.['application-id']}/environment/${params?.['environment-id']}/services`,
+ },
+ {
+ name: 'Pods',
+ link: `#`,
+ disable: true,
+ },
+ ];
+
+ return (
+ <>
+
+
+ // { }
+ // Add Pod
+ //
+ // }
+ />
+
+
+ {error && (
+
+ )}
+ >
+ );
+};
+
+export default Services;
diff --git a/src/app/applications/[application-id]/environment/[environment-id]/services/page.js b/src/app/applications/[application-id]/environment/[environment-id]/services/page.js
new file mode 100644
index 0000000..2cc148e
--- /dev/null
+++ b/src/app/applications/[application-id]/environment/[environment-id]/services/page.js
@@ -0,0 +1,96 @@
+'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 { formatTime } from '../../../../../../../utlis/helperFunc';
+import useServiceList from '../../../../../../../hooks/service/getServiceList';
+import ErrorComponent from '../../../../../../../components/ErrorComponent';
+import BreadCrumbComp from '../../../../../../../components/BreadCrumb';
+import { useParams } from 'next/navigation';
+import { AppContext } from '../../../../../../../libs/context';
+import ServiceDetails from '../../../../../../../partials/service/serviceDetail';
+
+const headers = [
+ { key: 'name', label: 'Name', align: 'left', colClassName: 'sm:w-1/4' },
+ { key: 'loadBalancerIP', label: 'Load Balance IP', align: 'left', colClassName: 'sm:w-1/4' },
+ { key: 'timestamps', label: 'Timestamps', align: 'left', colClassName: 'sm:w-1/3' },
+ { key: 'status', label: 'Status', align: 'end', colClassName: 'sm:w-1/4' },
+];
+
+const Services = () => {
+ const { services, loading, error } = useServiceList();
+ const { appData } = useContext(AppContext);
+ const params = useParams();
+
+ const data = appData?.APPLICATION_DATA?.data?.filter(
+ (item) => item.id === Number(params?.['application-id']),
+ )?.[0];
+
+ const bodyData = services?.data?.services?.map((item) => {
+ return {
+ name: item.metadata.name,
+ loadBalancerIP: item.spec.loadBalancerIP,
+ timestamps: formatTime(item.metadata.creationTimestamp),
+ status: item.status.loadBalancer.ingress ? (
+
+ Active
+
+ ) : (
+
+ Inactive
+
+ ),
+ };
+ });
+
+ const breadcrumbList = [
+ { name: 'Applications', link: '/applications' },
+ {
+ name: appData?.APPLICATION_DATA?.isSuccess ? data?.name : 'loading...',
+ link: `/applications/${params?.['application-id']}/environment`,
+ },
+ {
+ name: 'Services',
+ link: `#`,
+ disable: true,
+ },
+ ];
+
+ return (
+ <>
+
+
+ // { }
+ // Add Service
+ //
+ // }
+ />
+
+
+ {error && (
+
+ )}
+ >
+ );
+};
+
+export default Services;
diff --git a/src/app/applications/[application-id]/deploymentSpace/addEnvironment/page.js b/src/app/applications/[application-id]/environment/addEnvironment/page.js
similarity index 96%
rename from src/app/applications/[application-id]/deploymentSpace/addEnvironment/page.js
rename to src/app/applications/[application-id]/environment/addEnvironment/page.js
index 6868731..96e5334 100644
--- a/src/app/applications/[application-id]/deploymentSpace/addEnvironment/page.js
+++ b/src/app/applications/[application-id]/environment/addEnvironment/page.js
@@ -19,7 +19,7 @@ const CreateEnvironment = () => {
{ 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: 'Add Environment',
diff --git a/src/app/applications/[application-id]/deploymentSpace/page.js b/src/app/applications/[application-id]/environment/page.js
similarity index 92%
rename from src/app/applications/[application-id]/deploymentSpace/page.js
rename to src/app/applications/[application-id]/environment/page.js
index 5604d3d..9cedd49 100644
--- a/src/app/applications/[application-id]/deploymentSpace/page.js
+++ b/src/app/applications/[application-id]/environment/page.js
@@ -26,7 +26,8 @@ const DeploymentSpace = () => {
const { value, loading, error } = useGetDeploymentSpace();
- const handleAddEnvConfig = (id) => {
+ const handleAddEnvConfig = (e, id) => {
+ e.stopPropagation();
router.push(`${pathname}/${id}/configureDeploymentSpace`);
};
@@ -46,10 +47,9 @@ const DeploymentSpace = () => {
deployment_space: item?.deploymentSpace ? (
handleDeploymentList(item.deploymentSpace)
) : (
- //
handleAddEnvConfig(item.id)}
+ onClick={(e) => handleAddEnvConfig(e, item.id)}
>
@@ -67,6 +67,10 @@ const DeploymentSpace = () => {
(item) => item.id === Number(params?.['application-id']),
)?.[0];
+ const handleRowClick = (row) => {
+ router.push(`${pathname}/${row.id}/services`);
+ };
+
const breadcrumbList = [
{ name: 'Applications', link: '/applications' },
{
@@ -80,7 +84,7 @@ const DeploymentSpace = () => {
{
// onEdit={handleEdit}
onDelete={handleDelete}
action={false}
+ handleRowClick={handleRowClick}
+ enableRowClick={true}
/>
diff --git a/src/app/applications/[application-id]/layout.js b/src/app/applications/[application-id]/layout.js
index 0007747..aa3316c 100644
--- a/src/app/applications/[application-id]/layout.js
+++ b/src/app/applications/[application-id]/layout.js
@@ -5,8 +5,8 @@ import Sidebar from '../../../../partials/sideBar';
const Layout = ({ children }) => {
return (
-
-
+
+
{children}
diff --git a/src/app/globals.css b/src/app/globals.css
index c91c743..744faf0 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -34,3 +34,16 @@ body {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
+
+@keyframes loader {
+ 0% {
+ transform: translateX(-100%);
+ }
+ 100% {
+ transform: translateX(200%);
+ }
+}
+
+.animate-loader {
+ animation: loader 1.5s infinite linear;
+}
diff --git a/svg/cron.js b/svg/cron.js
new file mode 100644
index 0000000..b726bda
--- /dev/null
+++ b/svg/cron.js
@@ -0,0 +1,31 @@
+import React from 'react';
+
+export const Cron = ({ color = '#9197B3', ...rest }) => {
+ return (
+
+
+
+
+
+ );
+};
diff --git a/svg/deployment.js b/svg/deployment.js
new file mode 100644
index 0000000..8b5aafb
--- /dev/null
+++ b/svg/deployment.js
@@ -0,0 +1,23 @@
+import React from 'react';
+
+const DeploymentIcon = ({ color = '#9197B3', ...rest }) => {
+ return (
+
+
+
+ );
+};
+
+export default DeploymentIcon;
diff --git a/svg/deploymentSpace.js b/svg/environment.js
similarity index 94%
rename from svg/deploymentSpace.js
rename to svg/environment.js
index 95e447f..4aaea2d 100644
--- a/svg/deploymentSpace.js
+++ b/svg/environment.js
@@ -1,11 +1,11 @@
import React from 'react';
-export const DeploymentSpace = ({ color = '#9197B3', ...rest }) => {
+export const Environment = ({ color = '#9197B3', ...rest }) => {
return (
{
+ return (
+
+
+
+ );
+};
diff --git a/svg/services.js b/svg/services.js
new file mode 100644
index 0000000..061df64
--- /dev/null
+++ b/svg/services.js
@@ -0,0 +1,49 @@
+import React from 'react';
+
+export const Services = ({ color = '#9197B3', ...rest }) => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/utlis/apiEndpoints.js b/utlis/apiEndpoints.js
index 07b98fc..ba7f966 100644
--- a/utlis/apiEndpoints.js
+++ b/utlis/apiEndpoints.js
@@ -1,3 +1,4 @@
export const CLOUD_ACCOUNT_ENDPOINT = '/cloud-accounts';
export const APPLICATION_ENDPOINT = '/applications';
export const ENVIRONMENT_ENDPOINT = '/environments';
+export const DEPLOYMENT_ENDPOINT = '/deploymentspace/deployment';
diff --git a/utlis/helperFunc.js b/utlis/helperFunc.js
index 35a878a..ce78586 100644
--- a/utlis/helperFunc.js
+++ b/utlis/helperFunc.js
@@ -2,10 +2,12 @@ import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import utc from 'dayjs/plugin/utc';
import isBetween from 'dayjs/plugin/isBetween';
+import duration from 'dayjs/plugin/duration';
dayjs.extend(relativeTime);
dayjs.extend(utc);
dayjs.extend(isBetween); // Extend dayjs with the isBetween plugin
+dayjs.extend(duration);
const getOrdinalSuffix = (day) => {
if (day > 3 && day < 21) return 'th'; // Covers 11th, 12th, 13th, etc.
@@ -52,6 +54,24 @@ export const formatTime = (time, type) => {
}
};
+export const calculateAge = (creationTimestamp) => {
+ const now = dayjs();
+ const creationTime = dayjs(creationTimestamp);
+ const diffInMs = now.diff(creationTime);
+
+ // Calculate days, hours, minutes
+ const days = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
+ const hours = Math.floor((diffInMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
+ const minutes = Math.floor((diffInMs % (1000 * 60 * 60)) / (1000 * 60));
+
+ let result = '';
+ if (days > 0) result += `${days}d`;
+ if (hours > 0) result += `${result ? ' ' : ''}${hours}h`;
+ if (minutes > 0) result += `${result ? ' ' : ''}${minutes}m`;
+
+ return result || '0m'; // Default to "0m" if the difference is negligible
+};
+
export function ParseJSON(str) {
try {
if (typeof str !== 'string') {