Skip to content

Commit e9559b4

Browse files
committed
fix: change the CreateEditSite and ListSites pages to use the correct class
- Update routing to reflect two new modes - Use Site class instead of the component from the schema - Add an imported color picker
1 parent 4d7c997 commit e9559b4

File tree

5 files changed

+103
-28
lines changed

5 files changed

+103
-28
lines changed

src/admin/AdminBody.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ export default function AdminBody(props: AdminBodyProps) {
1818
return <EditData />;
1919
case 'list-sites':
2020
return <ListSites />;
21-
case 'create-edit-site':
22-
return <CreateEditSite />;
21+
case 'create-site':
22+
return <CreateEditSite mode="create" />;
23+
case 'new-edit-site':
24+
return <CreateEditSite mode="edit" />;
2325
default:
2426
return <h1>Error</h1>;
2527
}

src/admin/CreateEditSite.tsx

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react';
1+
import React, { useState, useEffect } from 'react';
22
import {
33
Box,
44
Container,
@@ -20,6 +20,7 @@ import {
2020
Add as AddIcon,
2121
Delete as DeleteIcon,
2222
} from '@mui/icons-material';
23+
import ColorPicker from 'react-pick-color';
2324

2425
interface CellEntry {
2526
id: string;
@@ -32,25 +33,43 @@ interface BoundaryPoint {
3233
lng: string;
3334
}
3435

35-
export default function CreateEditSite() {
36+
interface CreateEditSiteProps {
37+
mode: 'create' | 'edit';
38+
site?: Site;
39+
}
40+
41+
export default function CreateEditSite({ mode, site }: CreateEditSiteProps) {
3642
const [name, setName] = useState('');
3743
const [longitude, setLongitude] = useState('');
3844
const [latitude, setLatitude] = useState('');
3945
const [status, setStatus] = useState('');
4046
const [address, setAddress] = useState('');
4147
const [cells, setCells] = useState<CellEntry[]>([]);
4248
const [colorEnabled, setColorEnabled] = useState(true);
43-
const [colorValue, setColorValue] = useState('');
49+
const [colorValue, setColorValue] = useState('#fff');
4450
const [boundaryEnabled, setBoundaryEnabled] = useState(true);
4551
const [boundaryPoints, setBoundaryPoints] = useState<BoundaryPoint[]>([]);
4652

53+
4754
const handleBack = () => {
4855
console.log('Navigate back');
4956
window.open('/admin/list-sites', '_self');
5057
};
5158

5259
const handleSave = () => {
5360
console.log('Save site');
61+
if (validateSite()) {
62+
const site: Site = {
63+
name,
64+
latitude: parseFloat(latitude),
65+
longitude: parseFloat(longitude),
66+
status: status as SiteStatus,
67+
address,
68+
cell_id: cells.map(cell => cell.cellId),
69+
color: colorEnabled ? colorValue : undefined,
70+
boundary: boundaryEnabled ? boundaryPoints.map(point => [parseFloat(point.lat), parseFloat(point.lng)]) : undefined,
71+
};
72+
}
5473
};
5574

5675
const addCell = () => {
@@ -88,6 +107,34 @@ export default function CreateEditSite() {
88107
));
89108
};
90109

110+
const validateSite = () : boolean => {
111+
if (name === '') {
112+
alert('Name is required');
113+
return false;
114+
}
115+
if (longitude === '' || isNaN(Number(longitude))) {
116+
alert('Valid Longitude is required');
117+
return false;
118+
}
119+
if (latitude === '' || isNaN(Number(latitude))) {
120+
alert('Valid Latitude is required');
121+
return false;
122+
}
123+
if (status === '') {
124+
alert('Status is required');
125+
return false;
126+
}
127+
if (address === '') {
128+
alert('Address is required');
129+
return false;
130+
}
131+
if (cells.length === 0) {
132+
alert('At least one Cell ID is required');
133+
return false;
134+
}
135+
return true;
136+
}
137+
91138
return (
92139
<Container maxWidth="md" sx={{ mt: 4, mb: 4 }}>
93140
<Paper elevation={3} sx={{ p: 3 }}>
@@ -212,12 +259,7 @@ export default function CreateEditSite() {
212259
label="Color"
213260
/>
214261
{colorEnabled && (
215-
<TextField
216-
fullWidth
217-
value={colorValue}
218-
onChange={(e) => setColorValue(e.target.value)}
219-
sx={{ mt: 1 }}
220-
/>
262+
<ColorPicker color={colorValue} onChange={color => setColorValue(color.hex)} />
221263
)}
222264
</Box>
223265
<Box sx={{ mb: 3 }}>

src/admin/ListSites.tsx

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ import { Add as AddIcon } from '@mui/icons-material';
1414
import { apiClient } from '@/utils/fetch';
1515
import { components } from '@/types/api';
1616

17-
const parseSitesFromJSON = (jsonString: string): components['schemas']['Site'][] => {
17+
const parseSitesFromJSON = (jsonString: string): Site[] => {
1818
try {
1919
const parsed = JSON.parse(jsonString);
2020

2121
if (!Array.isArray(parsed.sites)) {
2222
throw new Error("Invalid format: 'sites' should be an array");
2323
}
2424

25-
const sites: components['schemas']['Site'][] = parsed.sites.map((site: any): components['schemas']['Site'] => {
25+
const sites: Site[] = parsed.sites.map((site: any): Site => {
2626
return {
2727
name: site.name,
2828
latitude: site.latitude,
@@ -43,10 +43,10 @@ const parseSitesFromJSON = (jsonString: string): components['schemas']['Site'][]
4343
};
4444

4545
export default function ListSites() {
46-
const [sites, setSites] = useState<components['schemas']['Site'][]>([]);
46+
const [sites, setSites] = useState<Site[]>([]);
4747
const handleEdit = (siteName: string) => {
4848
console.log(`Edit site with ID: ${siteName}`);
49-
window.open('/admin/create-edit-site', '_self');
49+
window.open('/admin/new-edit-site', '_self');
5050
};
5151

5252
const handleDelete = (siteName: string) => {
@@ -59,7 +59,7 @@ export default function ListSites() {
5959

6060
const handleAdd = () => {
6161
console.log('Add new site');
62-
window.open('/admin/create-edit-site', '_self')
62+
window.open('/admin/create-site', '_self')
6363
};
6464

6565
const reloadSites = () => {
@@ -81,9 +81,30 @@ export default function ListSites() {
8181
reloadSites();
8282
});
8383

84-
const deleteSite = (site: components['schemas']['Site']) => {
84+
const siteToSchema = (site: Site): components['schemas']['Site'] => {
85+
return {
86+
name: site.name,
87+
latitude: site.latitude,
88+
longitude: site.longitude,
89+
status: siteStatusToSchema(site.status),
90+
address: site.address,
91+
cell_id: site.cell_id,
92+
color: site.color,
93+
boundary: site.boundary
94+
};
95+
}
96+
97+
const siteStatusToSchema = (siteStatus: SiteStatus): components['parameters']['SiteStatus'] => {
98+
if (siteStatus === 'unknown') {
99+
throw new Error(`Invalid site status: ${siteStatus}`);
100+
} else {
101+
return siteStatus as components['parameters']['SiteStatus'];
102+
}
103+
}
104+
105+
const deleteSite = (site: Site) => {
85106
apiClient.DELETE('/api/secure-site', {
86-
body: site
107+
body: siteToSchema(site)
87108
}).then(res => {
88109
const { data, error } = res;
89110
if (error) {
@@ -97,9 +118,9 @@ export default function ListSites() {
97118
});
98119
};
99120

100-
const editSite = (site: components['schemas']['Site']) => {
121+
const editSite = (site: Site) => {
101122
apiClient.PUT('/api/secure-site', {
102-
body: site
123+
body: siteToSchema(site)
103124
}).then(res => {
104125
const { data, error } = res;
105126
if (error) {
@@ -113,9 +134,9 @@ export default function ListSites() {
113134
});
114135
};
115136

116-
const createSite = (site: components['schemas']['Site']) => {
137+
const createSite = (site: Site) => {
117138
apiClient.POST('/api/secure-site', {
118-
body: site
139+
body: siteToSchema(site)
119140
}).then(res => {
120141
const { data, error } = res;
121142
if (error) {
@@ -127,7 +148,7 @@ export default function ListSites() {
127148
}).catch(err => {
128149
console.error(`Error creating site: ${err}`);
129150
});
130-
}
151+
};
131152
return (
132153
<Container maxWidth='md' sx={{ mt: 4, mb: 4 }}>
133154
<Paper elevation={3} sx={{ p: 3 }}>

src/index.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ 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 ListSites from './admin/ListSites';
76
import './index.css';
87

98
// Get the root element
@@ -24,10 +23,17 @@ root.render(
2423
<Route path='/login' element={<Login />} />
2524
<Route path='/admin' element={<AdminPortal />} />
2625
<Route path='/admin/users' element={<AdminPortal page={'users'} />} />
27-
<Route path='/admin/list-sites' element={<ListSites />} />
2826
<Route
29-
path='/admin/create-edit-site'
30-
element={<AdminPortal page='create-edit-site' />}
27+
path='/admin/list-sites'
28+
element={<AdminPortal page='list-sites' />}
29+
/>
30+
<Route
31+
path='/admin/create-site'
32+
element={<AdminPortal page='create-site' />}
33+
/>
34+
<Route
35+
path='/admin/new-edit-site'
36+
element={<AdminPortal page='new-edit-site' />}
3137
/>
3238
<Route
3339
path='/admin/edit-site'

src/types.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
type LatLng = [number, number];
2+
13
type SiteOption = {
24
label: string;
35
value: string;
@@ -22,7 +24,7 @@ type DisplayOption = {
2224

2325
type SiteStatus = 'active' | 'confirmed' | 'in-conversation' | 'unknown';
2426

25-
type AdminPage = 'users' | 'edit-site' | 'edit-data' | 'list-sites' | 'create-edit-site';
27+
type AdminPage = 'users' | 'edit-site' | 'edit-data' | 'list-sites' | 'create-site' | 'new-edit-site';
2628

2729
type UserRow = {
2830
identity: string;
@@ -78,3 +80,5 @@ type Measurement = {
7880
site: string;
7981
device_id: number;
8082
};
83+
84+

0 commit comments

Comments
 (0)