Skip to content

Commit b8ad1e4

Browse files
committed
feat(admin): add new edit site page with server API integration
- Create new edit site page component - Implement interface for new server endpoints - Add initial API request parsing logic
1 parent 9967fd0 commit b8ad1e4

File tree

5 files changed

+174
-2
lines changed

5 files changed

+174
-2
lines changed

src/ListItems.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import ManageAccountsIcon from '@mui/icons-material/ManageAccounts';
77
import SyncIcon from '@mui/icons-material/Sync';
88
import HomeIcon from '@mui/icons-material/Home';
99
import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings';
10+
import EditIcon from '@mui/icons-material/Edit';
1011
import { ListItemButton } from '@mui/material';
1112

1213
export const mainListItems = (
@@ -30,6 +31,12 @@ export const mainListItems = (
3031
</ListItemIcon>
3132
<ListItemText primary='Update Data' />
3233
</ListItemButton>
34+
<ListItemButton onClick={() => window.open('/admin/new-edit-site', '_self')}>
35+
<ListItemIcon>
36+
<EditIcon />
37+
</ListItemIcon>
38+
<ListItemText primary='New Edit Site' />
39+
</ListItemButton>
3340
</div>
3441
);
3542

src/admin/AdminBody.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import UserPage from './UserPage';
22
import EditSite from './EditSite';
33
import EditData from './EditData';
4+
import NewEditSite from './NewEditSite';
45

56
interface AdminBodyProps {
67
page: AdminPage;
@@ -14,6 +15,8 @@ export default function AdminBody(props: AdminBodyProps) {
1415
return <EditSite />;
1516
case 'edit-data':
1617
return <EditData />;
18+
case 'new-edit-site':
19+
return <NewEditSite />;
1720
default:
1821
return <h1>Error</h1>;
1922
}

src/admin/NewEditSite.tsx

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import { useEffect, useState } from 'react';
2+
import {
3+
Box,
4+
Container,
5+
Paper,
6+
Typography,
7+
Button,
8+
Fab,
9+
List,
10+
ListItem,
11+
ListItemText,
12+
} from '@mui/material';
13+
import { Add as AddIcon } from '@mui/icons-material';
14+
import { apiClient } from '@/utils/fetch';
15+
16+
const parseSitesFromJSON = (jsonString: string): Site[] => {
17+
try {
18+
const parsed = JSON.parse(jsonString);
19+
20+
if (!Array.isArray(parsed.sites)) {
21+
throw new Error("Invalid format: 'sites' should be an array");
22+
}
23+
24+
const sites: Site[] = parsed.sites.map((site: any): Site => {
25+
return {
26+
name: site.name,
27+
latitude: site.latitude,
28+
longitude: site.longitude,
29+
status: site.status as SiteStatus,
30+
address: site.address,
31+
cell_id: site.cell_id,
32+
color: site.color,
33+
boundary: site.boundary?.map((point: any) => ({
34+
lat: point.lat,
35+
lng: point.lng
36+
})) ?? [],
37+
};
38+
});
39+
40+
return sites;
41+
} catch (error) {
42+
console.error("Failed to parse sites JSON:", error);
43+
return [];
44+
}
45+
};
46+
47+
48+
export default function NewEditSite() {
49+
const [sites, setSites] = useState<Site[]>([]);
50+
const handleEdit = (siteName: string) => {
51+
console.log(`Edit site with ID: ${siteName}`);
52+
};
53+
54+
const handleDelete = (siteName: string) => {
55+
console.log(`Delete site with ID: ${siteName}`);
56+
};
57+
58+
const handleAdd = () => {
59+
console.log('Add new site');
60+
};
61+
62+
const reloadSites = () => {
63+
apiClient
64+
.GET('/api/public-sites')
65+
.then(res => {
66+
const { data, error } = res;
67+
if (error || !data) {
68+
console.log(`Unable to query sites: ${error}`);
69+
return;
70+
}
71+
setSites(parseSitesFromJSON(JSON.stringify(data)));
72+
})
73+
.catch(err => {
74+
return <div></div>;
75+
});
76+
};
77+
useEffect(() => {
78+
reloadSites();
79+
});
80+
81+
return (
82+
<Container maxWidth="md" sx={{ mt: 4, mb: 4 }}>
83+
<Paper elevation={3} sx={{ p: 3 }}>
84+
<Typography variant="h4" component="h1" gutterBottom>
85+
Site Management
86+
</Typography>
87+
88+
<List>
89+
{sites.map((site) => (
90+
<ListItem
91+
key={site.name}
92+
sx={{
93+
border: '1px solid #e0e0e0',
94+
borderRadius: 1,
95+
mb: 2,
96+
bgcolor: 'background.paper',
97+
}}
98+
>
99+
<ListItemText
100+
primary={
101+
<Typography variant="h6" component="div">
102+
{site.name}
103+
</Typography>
104+
}
105+
sx={{ flexGrow: 1 }}
106+
/>
107+
<Box sx={{ display: 'flex', gap: 1, ml: 2 }}>
108+
<Button
109+
variant="contained"
110+
color="warning"
111+
onClick={() => handleEdit(site.name)}
112+
sx={{
113+
backgroundColor: '#d4af37',
114+
color: 'black',
115+
'&:hover': {
116+
backgroundColor: '#b8941f',
117+
},
118+
}}
119+
>
120+
Edit
121+
</Button>
122+
<Button
123+
variant="contained"
124+
color="error"
125+
onClick={() => handleDelete(site.name)}
126+
sx={{
127+
backgroundColor: '#d32f2f',
128+
'&:hover': {
129+
backgroundColor: '#b71c1c',
130+
},
131+
}}
132+
>
133+
Delete
134+
</Button>
135+
</Box>
136+
</ListItem>
137+
))}
138+
</List>
139+
</Paper>
140+
141+
{/* Floating Action Button */}
142+
<Fab
143+
color="primary"
144+
aria-label="add"
145+
onClick={handleAdd}
146+
sx={{
147+
position: 'fixed',
148+
bottom: 16,
149+
right: 16,
150+
backgroundColor: '#4caf50',
151+
'&:hover': {
152+
backgroundColor: '#388e3c',
153+
},
154+
}}
155+
>
156+
<AddIcon />
157+
</Fab>
158+
</Container>
159+
);
160+
}

src/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom';
33
import Vis from './vis/Vis';
44
import Login from './admin/Login';
55
import AdminPortal from './admin/AdminPortal';
6+
import NewEditSite from './admin/NewEditSite';
67
import './index.css';
78

89
// Get the root element
@@ -23,6 +24,7 @@ root.render(
2324
<Route path='/login' element={<Login />} />
2425
<Route path='/admin' element={<AdminPortal />} />
2526
<Route path='/admin/users' element={<AdminPortal page={'users'} />} />
27+
<Route path='/admin/new-edit-site' element={<NewEditSite />} />
2628
<Route
2729
path='/admin/edit-site'
2830
element={<AdminPortal page='edit-site' />}

src/types.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type DisplayOption = {
2222

2323
type SiteStatus = 'active' | 'confirmed' | 'in-conversation' | 'unknown';
2424

25-
type AdminPage = 'users' | 'edit-site' | 'edit-data';
25+
type AdminPage = 'users' | 'edit-site' | 'edit-data' | 'new-edit-site' | 'test';
2626

2727
type UserRow = {
2828
identity: string;
@@ -77,4 +77,4 @@ type Measurement = {
7777
ping: number;
7878
site: string;
7979
device_id: number;
80-
};
80+
};

0 commit comments

Comments
 (0)