diff --git a/app/(api)/api/page.tsx b/app/(api)/api/page.tsx
index 1f231c48..28005a58 100644
--- a/app/(api)/api/page.tsx
+++ b/app/(api)/api/page.tsx
@@ -1,7 +1,6 @@
"use client";
-
-import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
+import { useState } from "react";
const DynamicReactSwagger = dynamic(() => import("./react-swagger"), {
ssr: false,
@@ -10,23 +9,33 @@ const DynamicReactSwagger = dynamic(() => import("./react-swagger"), {
export default function IndexPage() {
const [spec, setSpec] = useState(null);
+ const [error, setError] = useState(false);
- useEffect(() => {
- async function fetchSpecs() {
+ (async function fetchSpecs() {
+ if (spec || error) return;
+ try {
const response = await fetch("swagger/doc.json");
- if (response.ok) {
+ if (!response.ok) throw new Error("Failed to fetch API docs");
const fetchedSpec = await response.json();
setSpec(fetchedSpec);
- } else {
- console.error("Failed to fetch API docs");
- }
+ } catch (err) {
+ console.error("Error fetching Swagger specs:", err);
+ setError(true);
}
-
- fetchSpecs();
- }, []);
-
- if (!spec) return
Loading...
;
+ })();
+
+ if (error) {
+ return Error loading API documentation. Please try again later.
;
+ }
+
+ if (!spec) {
+ return (
+
+ Loading API documentation...
+
+ );
+ }
return (
diff --git a/components/NetworkSliceModal.tsx b/components/NetworkSliceModal.tsx
index bf6df4ee..bf42e86c 100644
--- a/components/NetworkSliceModal.tsx
+++ b/components/NetworkSliceModal.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from "react";
+import React, { useState } from "react";
import {
Input,
Notification,
@@ -36,8 +36,8 @@ const NetworkSliceModal = ({ networkSlice, toggleModal, onSave }: NetworkSliceMo
const auth = useAuth()
const queryClient = useQueryClient();
const [apiError, setApiError] = useState(null);
- const [upfApiError, setUpfApiError] = useState(null);
const [gnbApiError, setGnbApiError] = useState(null);
+ const [upfApiError, setUpfApiError] = useState(null);
const NetworkSliceSchema = Yup.object().shape({
name: Yup.string()
@@ -58,10 +58,16 @@ const NetworkSliceModal = ({ networkSlice, toggleModal, onSave }: NetworkSliceMo
.required("MNC is required."),
upf: Yup.object()
.shape({ hostname: Yup.string().required("Please select a UPF.") })
+ .shape({ port: Yup.string().required("Please select a UPF.") })
.required("Selecting a UPF is required."),
gnbList: Yup.array()
- .min(1)
- .required("Selecting at least 1 gNodeB is required."),
+ .of(
+ Yup.object().shape({
+ name: Yup.string().required("gNodeB name is required."),
+ tac: Yup.string().required("gNodeB TAC is required."),
+ })
+ )
+ .min(1, "Selecting at least 1 gNodeB is required."),
});
const modalTitle = () => {
@@ -94,6 +100,14 @@ const NetworkSliceModal = ({ networkSlice, toggleModal, onSave }: NetworkSliceMo
validationSchema: NetworkSliceSchema,
onSubmit: async (values) => {
try {
+ if (upfItems.length === 0) {
+ setUpfApiError("No available UPF. Please add at least one UPF.");
+ return;
+ }
+ if (gnbItems.length === 0) {
+ setGnbApiError("No available GNB. Please add at least one GNB.");
+ return;
+ }
if (networkSlice) {
await editNetworkSlice({
name: values.name,
@@ -128,55 +142,29 @@ const NetworkSliceModal = ({ networkSlice, toggleModal, onSave }: NetworkSliceMo
},
});
- const { data: upfList = [], isLoading: isUpfLoading, isError: isUpfError } = useQuery({
+ const upfQuery = useQuery({
queryKey: [queryKeys.upfList, auth.user?.authToken],
- queryFn: () => getUpfList(auth.user ? auth.user.authToken : ""),
- enabled: auth.user ? true : false,
+ queryFn: () => getUpfList(auth.user!.authToken),
+ enabled: auth.user ? true : false
});
- useEffect(() => {
- const checkUpfList = async () => {
- if (isUpfError) {
- setUpfApiError("Failed to retrieve the list of UPFs from the server.")
- } else if (!isUpfLoading && upfList.length === 0) {
- setUpfApiError("No available UPF. Please add at least one UPF.");
- }
- };
- checkUpfList();
- }, [isUpfLoading, isUpfError, upfList]);
-
- const { data: gnbList = [], isLoading: isGnbLoading, isError: isGnbError } = useQuery({
+ const gnbQuery = useQuery({
queryKey: [queryKeys.gnbList, auth.user?.authToken],
- queryFn: () => getGnbList(auth.user ? auth.user.authToken : ""),
- enabled: auth.user ? true : false,
+ queryFn: () => getGnbList(auth.user!.authToken),
+ enabled: auth.user ? true : false
});
- useEffect(() => {
- const checkGnbList = async () => {
- if (isGnbError) {
- setGnbApiError("Failed to retrieve the list of GNBs from the server.")
- } else if (!isGnbLoading && gnbList.length === 0) {
- setGnbApiError("No available GNB. Please add at least one GNB.");
- }
- };
- checkGnbList();
- }, [isGnbLoading, isGnbError, gnbList]);
+ const upfItems = (upfQuery.data as UpfItem[]) || [];
+ const gnbItems = (gnbQuery.data as GnbItem[]) || [];
const handleUpfChange = (e: React.ChangeEvent) => {
- const upf = upfList.find(
- (item) => e.target.value === `${item.hostname}:${item.port}`,
- );
+ const upf = upfItems.find((item: UpfItem) => e.target.value === `${item.hostname}:${item.port}`);
void formik.setFieldValue("upf", upf);
};
const handleGnbChange = (e: React.ChangeEvent) => {
- const selectedOptions = Array.from(e.target.selectedOptions);
- const items = gnbList.filter((item) =>
- selectedOptions.some(
- (option) => option.value === `${item.name}:${item.tac}`,
- ),
- );
- void formik.setFieldValue("gnbList", items);
+ const gnb = gnbItems.find((item: GnbItem) => e.target.value === `${item.name}:${item.tac}`);
+ void formik.setFieldValue("gnbList", [...formik.values.gnbList, gnb]);
};
const getGnbListValueAsString = () => {
@@ -189,6 +177,42 @@ const NetworkSliceModal = ({ networkSlice, toggleModal, onSave }: NetworkSliceMo
return formik.values.upf.hostname ? `${formik.values.upf.hostname}:${formik.values.upf.port}` : "";
};
+ const ErrorNotification = ({
+ error,
+ }: {
+ error: string | null;
+ }) => {
+ return error ? (
+
+ {error}
+
+ ) : null;
+ };
+
+ if (upfQuery.isLoading || gnbQuery.isLoading) {
+ return (
+
+ Fetching data...
+
+ );
+ }
+
+ if (upfQuery.isError) {
+ return (
+
+ Failed to retrieve the list of UPFs from the server.
+
+ );
+ }
+
+ if (gnbQuery.isError) {
+ return (
+
+ Failed to retrieve the list of gNodeBs from the server.
+
+ );
+ }
+
return (
}
>
- {apiError && (
-
- {apiError}
-
- )}
- {upfApiError && (
-
- {upfApiError}
-
- )}
- {gnbApiError && (
-
- {gnbApiError}
-
- )}
+ {apiError && }
+ {upfApiError && }
+ {gnbApiError && }