diff --git a/app/(nms)/network-configuration/page.tsx b/app/(nms)/network-configuration/page.tsx index de69a2a4..f82f0b02 100644 --- a/app/(nms)/network-configuration/page.tsx +++ b/app/(nms)/network-configuration/page.tsx @@ -1,5 +1,5 @@ "use client"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { Button, Card, @@ -11,24 +11,27 @@ import NetworkSliceModal from "@/components/NetworkSliceModal"; import NetworkSliceEmptyState from "@/components/NetworkSliceEmptyState"; import { NetworkSliceTable } from "@/components/NetworkSliceTable"; import Loader from "@/components/Loader"; -import { useQuery, useQueryClient } from "@tanstack/react-query"; import { queryKeys } from "@/utils/queryKeys"; import PageHeader from "@/components/PageHeader"; import PageContent from "@/components/PageContent"; import { NetworkSlice } from "@/components/types"; import { useAuth } from "@/utils/auth"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { apiPostNetworkSlice } from "@/utils/callNetworkSliceApi"; const NetworkConfiguration = () => { const queryClient = useQueryClient(); const [isCreateModalVisible, setCreateModalVisible] = useState(false); const [isEditModalVisible, setEditModalVisible] = useState(false); const [networkSlice, setNetworkSlice] = useState(undefined); + const [refresh, setRefresh] = useState(false); // State to track refresh (optional if using mutation directly) const auth = useAuth() + // Fetching Network Slices using useQuery const { data: networkSlices = [], isLoading: loading, status: networkSlicesQueryStatus, error: networkSlicesQueryError } = useQuery({ queryKey: [queryKeys.networkSlices, auth.user?.authToken], queryFn: () => getNetworkSlices(auth.user ? auth.user.authToken : ""), - enabled: auth.user ? true : false, + enabled: !!auth.user, // Only enable if the user is authenticated retry: (failureCount, error): boolean => { if (error.message.includes("401")) { return false @@ -36,12 +39,37 @@ const NetworkConfiguration = () => { return true } }); - if (networkSlicesQueryStatus == "error") { - if (networkSlicesQueryError.message.includes("401")) { - auth.logout() - } - return

{networkSlicesQueryError.message}

- } + + // Mutation hook to add a new Network Slice + const addNetworkSlice = async (newSlice: NetworkSlice): Promise => { + return await apiPostNetworkSlice(newSlice["slice-name"], newSlice, auth.user?.authToken || ""); + }; + + const addNetworkSliceMutation = useMutation({ + mutationFn: addNetworkSlice, + onSuccess: () => { + // Invalidate and refetch the network slices query + queryClient.invalidateQueries({ queryKey: [queryKeys.networkSlices] }); + + // Optionally trigger refresh via state (if more actions depend on it) + setRefresh(true); + }, + onError: (error) => { + console.error("Error adding network slice:", error); + }, + }); + + // Trigger UI refresh when refresh flag is set + useEffect(() => { + if (refresh) { + setRefresh(false); // Reset refresh state + setCreateModalVisible(false); // Close modal after adding network slice + } + }, [refresh]); + + const handleAddNetworkSlice = (newSlice: NetworkSlice) => { + addNetworkSliceMutation.mutate(newSlice); + }; const toggleCreateNetworkSliceModal = () => setCreateModalVisible((prev) => !prev); @@ -115,6 +143,13 @@ const NetworkConfiguration = () => { return ; } + if (networkSlicesQueryStatus == "error") { + if (networkSlicesQueryError.message.includes("401")) { + auth.logout() + } + return

{networkSlicesQueryError.message}

+ } + return ( <> {networkSlices.length > 0 && ( @@ -145,6 +180,7 @@ const NetworkConfiguration = () => { {isCreateModalVisible && ( )} {isEditModalVisible && ( diff --git a/app/(nms)/subscribers/page.tsx b/app/(nms)/subscribers/page.tsx index 2fe0ed0a..0c2104af 100644 --- a/app/(nms)/subscribers/page.tsx +++ b/app/(nms)/subscribers/page.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { Button, MainTable, @@ -13,7 +13,7 @@ import { getNetworkSlices } from "@/utils/getNetworkSlices"; import SyncOutlinedIcon from "@mui/icons-material/SyncOutlined"; import { deleteSubscriber } from "@/utils/deleteSubscriber"; import Loader from "@/components/Loader"; -import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { queryKeys } from "@/utils/queryKeys"; import PageHeader from "@/components/PageHeader"; import PageContent from "@/components/PageContent"; @@ -24,10 +24,29 @@ export type Subscriber = { ueId: string; }; +const addSubscriber = async (newSubscriber: void, token: string | undefined) => { + const response = await fetch("/api/subscribers", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify(newSubscriber), + }); + if (!response.ok) { + const errorBody = await response.json().catch(() => ({})); + throw new Error( + errorBody.message || "Failed to add subscriber" + ); + } + return response.json(); +}; + const Subscribers = () => { const queryClient = useQueryClient(); const [isCreateModalVisible, setCreateModalVisible] = useState(false); const [isEditModalVisible, setEditModalVisible] = useState(false); + const [newSubscriberAdded, setNewSubscriberAdded] = useState(false); // Tracks if updates are needed const [subscriber, setSubscriber] = useState(undefined); const auth = useAuth() @@ -43,6 +62,33 @@ const Subscribers = () => { } }); + // Mutation to add a subscriber + const mutation = useMutation({ + mutationFn: (newSubscriber) => addSubscriber(newSubscriber, auth.user?.authToken), + onSuccess: () => { + // On successful addition, invalidate the query to refresh + queryClient.invalidateQueries({ queryKey: [queryKeys.subscribers] }); + setNewSubscriberAdded(true); // Indicate that new data is added + }, + }); + + // Effect to handle state changes + useEffect(() => { + if (newSubscriberAdded) { + queryClient.invalidateQueries({ queryKey: [queryKeys.subscribers] }); + setNewSubscriberAdded(false); + } + }, [newSubscriberAdded, queryClient]); // `queryClient` is now included in the dependency array. + + const handleCreateSubscriber = async (newSubscriber: void) => { + try { + await mutation.mutateAsync(newSubscriber); // Trigger mutation + setCreateModalVisible(false); // Close the modal + } catch (error) { + console.error("Error adding subscriber:", error); // Handle error + } + }; + const { data: deviceGroups = [], isLoading: isDeviceGroupsLoading } = useQuery({ queryKey: [queryKeys.deviceGroups, auth.user?.authToken], queryFn: () => getDeviceGroups(auth.user ? auth.user.authToken : ""), @@ -165,10 +211,17 @@ const Subscribers = () => { rows={tableContent} /> - {isCreateModalVisible && } + {isCreateModalVisible && ( + handleCreateSubscriber(newSubscriber)} + slices={slices} + deviceGroups={deviceGroups} + />) + } {isEditModalVisible && } ); }; -export default Subscribers; +export default Subscribers; \ No newline at end of file diff --git a/components/NetworkSliceModal.tsx b/components/NetworkSliceModal.tsx index a2183bfc..99776175 100644 --- a/components/NetworkSliceModal.tsx +++ b/components/NetworkSliceModal.tsx @@ -27,11 +27,12 @@ interface NetworkSliceValues { } interface NetworkSliceModalProps { - networkSlice?: NetworkSlice; - toggleModal: () => void; + networkSlice?: NetworkSlice, + toggleModal: () => void, + onSave?: (newSlice: NetworkSlice) => void } -const NetworkSliceModal = ({ networkSlice, toggleModal }: NetworkSliceModalProps) => { +const NetworkSliceModal = ({ networkSlice, toggleModal, onSave }: NetworkSliceModalProps) => { const auth = useAuth() const queryClient = useQueryClient(); const [apiError, setApiError] = useState(null); diff --git a/components/SubscriberModal.tsx b/components/SubscriberModal.tsx index e3ad1d1b..8916295d 100644 --- a/components/SubscriberModal.tsx +++ b/components/SubscriberModal.tsx @@ -27,13 +27,14 @@ interface SubscriberValues { } type Props = { - toggleModal: () => void; - subscriber?: any; - slices: NetworkSlice[]; - deviceGroups: any[] + toggleModal: () => void, + subscriber?: any, + slices: NetworkSlice[], + deviceGroups: any[], + onSubmit?: (newSubscriber: any) => Promise }; -const SubscriberModal = ({ toggleModal, subscriber, slices, deviceGroups }: Props) => { +const SubscriberModal = ({ toggleModal, subscriber, slices, deviceGroups, onSubmit }: Props) => { const queryClient = useQueryClient(); const auth = useAuth() const [apiError, setApiError] = useState(null); diff --git a/package-lock.json b/package-lock.json index 050fd5a8..e3566e1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,6 +39,7 @@ }, "devDependencies": { "@types/swagger-ui-react": "^4.18.2", + "immutable": "^5.0.3", "prettier": "^3.4.2", "prettier-plugin-tailwindcss": "^0.6.9" } @@ -5209,10 +5210,9 @@ } }, "node_modules/immutable": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.2.tgz", - "integrity": "sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA==", - "peer": true + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", + "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==" }, "node_modules/import-fresh": { "version": "3.3.0", @@ -6523,12 +6523,6 @@ "node": ">=12.20.0" } }, - "node_modules/openapi-types": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", - "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", - "peer": true - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -7696,11 +7690,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/sass/node_modules/immutable": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.2.tgz", - "integrity": "sha512-1NU7hWZDkV7hJ4PJ9dur9gTNQ4ePNPN4k9/0YhwjzykTi/+3Q5pF93YU5QoVj8BuOnhLgaY8gs0U2pj4kSYVcw==" - }, "node_modules/sass/node_modules/readdirp": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", diff --git a/package.json b/package.json index 1cbc0a55..b2b4fb62 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ }, "devDependencies": { "@types/swagger-ui-react": "^4.18.2", + "immutable": "^5.0.3", "prettier": "^3.4.2", "prettier-plugin-tailwindcss": "^0.6.9" }