diff --git a/app/components/Map.tsx b/app/components/Map.tsx
index caae758..aee3971 100644
--- a/app/components/Map.tsx
+++ b/app/components/Map.tsx
@@ -29,6 +29,8 @@ import {
initializePlayground,
replacePlayground,
replaceOldPlayground,
+ clearToUpdatePlayground,
+ replaceToUpdatePlayground,
} from '../../lib/features/playground/playgroundSlice';
import {
@@ -139,6 +141,8 @@ export default function Map() {
const antennasData = useAppSelector((state) => state.currentAntennas.value);
+ const toBeUpdated = useAppSelector((state) => state.playground.toBeUpdated);
+
const dispatch = useAppDispatch();
useEffect(() => {
@@ -151,7 +155,7 @@ export default function Map() {
}
const responseJson = (await response.json()) as Antenna[];
- console.log('Antennas Data:', responseJson);
+
return responseJson;
} catch (e) {
if (e instanceof Error) {
@@ -182,9 +186,23 @@ export default function Map() {
})
);
+ const playgroundAntennasData: AccessPoint[] = accessPoints.map(
+ (ap: Antenna) => ({
+ id: ap.id,
+ modelName: ap.modelname,
+ lat: ap.latitude,
+ lon: ap.longitude,
+ frequency: ap.playground_frequency || 0,
+ azimuth: ap.azimuth || 0,
+ antenna_status: ap.antenna_status || 'N/A',
+ cpu: ap.cpu || -1,
+ ram: ap.ram || -1,
+ })
+ );
+
if (!initialized.current) {
store.dispatch(initializeActual(antennasData));
- store.dispatch(initializePlayground(antennasData));
+ store.dispatch(initializePlayground(playgroundAntennasData));
store.dispatch(initializeCurrent(antennasData));
initialized.current = true;
}
@@ -212,7 +230,8 @@ export default function Map() {
fetchDataAndSetAntennasData().catch((error) => {
console.error(error);
});
- }, [store]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
useEffect(() => {
const sectorlobesData: SectorlobeData[] = antennasData.data.map((ap) => {
@@ -346,6 +365,43 @@ export default function Map() {
return amount;
}, [intersections]);
+ const handleSavePlayground = async () => {
+ const url = '/api/v1/antenna/';
+ if (toBeUpdated.length === 0) {
+ alert('No changes to save');
+ return;
+ }
+ for (let i = 0; i < 3; i++) {
+ try {
+ const response = await fetch(url, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ dataArray: toBeUpdated }),
+ });
+ if (!response.ok) {
+ throw new Error(
+ `${response.status} error: Failed to save playground data`
+ );
+ }
+ dispatch(replacePlayground(antennasData.data));
+ dispatch(replaceOldPlayground(antennasData.data));
+ dispatch(clearToUpdatePlayground());
+ alert('Playground data saved successfully');
+ break;
+ } catch (e) {
+ if (e instanceof Error) {
+ console.error(`Attempt ${i + 1} failed: ${e.message}`);
+ if (i === 2) {
+ alert('Failed to save playground data');
+ }
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+ }
+ }
+ }
+ };
+
return (
<>
@@ -431,48 +487,88 @@ export default function Map() {
-
Current mode:
-
+ {antennasData.mode === 'playground' ? (
+
+ {
+ if (toBeUpdated.length > 0) {
+ dispatch(replaceToUpdatePlayground([]));
+ }
+ const updatedData: AccessPoint[] = actualData.filter(
+ (antenna) => {
+ return (
+ playgroundData.find(
+ (oldAntenna) =>
+ oldAntenna.id === antenna.id &&
+ oldAntenna.frequency === antenna.frequency &&
+ oldAntenna.azimuth === antenna.azimuth
+ ) === undefined
+ );
+ }
);
- } else {
- dispatch(replacePlayground(antennasData.data));
+ if (updatedData.length > 0) {
+ dispatch(replaceToUpdatePlayground(updatedData));
+ }
dispatch(
changeCurrent({
- mode: 'actual',
+ mode: 'playground',
data: actualData,
})
);
- }
- }}
- >
- {antennasData.mode === 'actual' ? 'Current' : 'Playground'}
-
-
- {antennasData.mode === 'playground' ? (
-
+ }}
+ >
+ Revert All Changes
+
{
dispatch(
changeCurrent({ mode: 'playground', data: oldPlaygroundData })
);
+ dispatch(clearToUpdatePlayground());
}}
>
- Revert Changes
+ Revert Current Changes
{
- dispatch(replaceOldPlayground(antennasData.data));
- }}
+ onClick={() => void handleSavePlayground()}
>
Save Changes
diff --git a/app/components/SectorLobe.tsx b/app/components/SectorLobe.tsx
index e07466b..98442e5 100644
--- a/app/components/SectorLobe.tsx
+++ b/app/components/SectorLobe.tsx
@@ -9,6 +9,7 @@ import { SectorLobeProps } from '../types';
import { useAppSelector, useAppDispatch } from '../../lib/hooks';
import { updateCurrent } from '@/lib/features/currentAntennas/currentAntennasSlice';
+import { addToUpdatePlayground } from '@/lib/features/playground/playgroundSlice';
export default function SectorLobe({
key_path,
@@ -152,14 +153,17 @@ export default function SectorLobe({
const newAp = { ...currentAp };
newAp.azimuth = tempHeading;
newAp.frequency = tempFreq;
-
setCurrentAp(newAp);
if (currentMode === 'playground') {
+ dispatch(addToUpdatePlayground(newAp));
dispatch(updateCurrent(newAp));
}
}
- function handleCancel() {
+ function handleCancel(
+ e: React.FormEvent
| React.MouseEvent
+ ) {
+ e.preventDefault();
setTempHeading(heading);
setTempFreq(freq);
}
@@ -169,14 +173,14 @@ export default function SectorLobe({
positions={sectorVertices}
color={color}
fillOpacity={0.5}
- weight={1}
+ weight={3}
key={String(key_path)}
>
diff --git a/lib/features/playground/playgroundSlice.ts b/lib/features/playground/playgroundSlice.ts
index 594e374..d952114 100644
--- a/lib/features/playground/playgroundSlice.ts
+++ b/lib/features/playground/playgroundSlice.ts
@@ -2,13 +2,18 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit';
import { AccessPoint } from '../../../app/types';
-type PlaygroundState = { value: AccessPoint[]; old: AccessPoint[] };
+type PlaygroundState = {
+ value: AccessPoint[];
+ old: AccessPoint[];
+ toBeUpdated: AccessPoint[];
+};
export const playgroundSlice = createSlice({
name: 'playground',
initialState: {
value: [],
old: [],
+ toBeUpdated: [],
} satisfies PlaygroundState as PlaygroundState,
reducers: {
initializePlayground: (state, action: PayloadAction) => {
@@ -38,6 +43,39 @@ export const playgroundSlice = createSlice({
replaceOldPlayground: (state, action: PayloadAction) => {
return { ...state, old: action.payload };
},
+ addToUpdatePlayground: (state, action: PayloadAction) => {
+ for (let i = 0; i < state.toBeUpdated.length; i++) {
+ if (state.toBeUpdated[i].id === action.payload.id) {
+ return {
+ ...state,
+ toBeUpdated: state.toBeUpdated.map((item) => {
+ if (item.id === action.payload.id) {
+ return action.payload;
+ }
+ return item;
+ }),
+ };
+ }
+ }
+ return { ...state, toBeUpdated: [...state.toBeUpdated, action.payload] };
+ },
+ clearToUpdatePlayground: (state) => {
+ return { ...state, toBeUpdated: [] };
+ },
+ removeFromUpdatePlayground: (state, action: PayloadAction) => {
+ return {
+ ...state,
+ toBeUpdated: state.toBeUpdated.filter(
+ (item) => item !== action.payload
+ ),
+ };
+ },
+ replaceToUpdatePlayground: (
+ state,
+ action: PayloadAction
+ ) => {
+ return { ...state, toBeUpdated: action.payload };
+ },
},
});
@@ -48,6 +86,10 @@ export const {
removePlayground,
updatePlayground,
replaceOldPlayground,
+ addToUpdatePlayground,
+ clearToUpdatePlayground,
+ removeFromUpdatePlayground,
+ replaceToUpdatePlayground,
} = playgroundSlice.actions;
export default playgroundSlice.reducer;